Compare commits

68 Commits

Author SHA1 Message Date
X1627315083@163.com
59da67e4b4 Merge remote-tracking branch 'origin/StableVersion' into dev_vite 2026-02-06 16:46:37 +08:00
X1627315083@163.com
1428f191dd fix 2026-02-06 14:17:46 +08:00
李志鹏
13024cdd99 画布loading 2026-02-06 13:07:06 +08:00
李志鹏
fd85ea02c1 画布loading 2026-02-06 13:05:19 +08:00
李志鹏
c196ab6678 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-02-06 11:11:06 +08:00
李志鹏
c005b85c06 设置画布loading 2026-02-06 11:11:04 +08:00
李志鹏
b50dbbc246 去掉部件选取 2026-02-06 10:36:31 +08:00
X1627315083@163.com
01d09f4c34 修复overall印花和画布中印花scale不同 2026-02-05 17:38:27 +08:00
X1627315083@163.com
79c9a66296 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-02-05 16:49:50 +08:00
X1627315083@163.com
761b1b3512 Merge remote-tracking branch 'origin/StableVersion' into dev_vite 2026-02-05 16:49:47 +08:00
X1627315083@163.com
b2cb7378d6 修复overall模式角度设置不上 2026-02-05 16:41:56 +08:00
4d9ea75146 chore: i18n内容 2026-02-05 16:30:24 +08:00
f7e6926ee9 bugfix: events i18n 2026-02-05 16:25:12 +08:00
7aba4e30c9 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-02-05 10:33:47 +08:00
dc1ab330cf AiDA跳转竞赛页面 2026-02-05 10:33:42 +08:00
李志鹏
18c70fe6a3 1 2026-02-05 10:32:56 +08:00
李志鹏
5c746aca4d fix 2026-02-05 10:27:53 +08:00
李志鹏
72c4898101 部件选取框选工具合并操作 2026-02-05 10:26:40 +08:00
X1627315083@163.com
a905971dae 测试 2026-02-05 10:16:52 +08:00
69643dbc83 chore: prettier配置 2026-02-05 10:07:39 +08:00
f3a707d6d8 feat: 上传过程中不允许删除文件 2026-02-05 10:07:30 +08:00
8f4a43db14 feat: size改为form内 2026-02-04 17:30:09 +08:00
186a158114 chore: 中文版竞赛海报 2026-02-04 17:27:20 +08:00
3da4a97400 bugfix: i18n问题 2026-02-04 16:10:10 +08:00
X1627315083@163.com
96b3636aea 取消3d 拼贴功能 2026-02-04 15:59:45 +08:00
228e3d56b5 bugfix: 参赛表单i18n 2026-02-04 13:50:55 +08:00
99ea7eedc7 bugfix: award参赛表单 2026-02-04 13:39:13 +08:00
d4fb435db9 feat: 参赛表单页面i18n 2026-02-04 13:33:05 +08:00
0c8b3ee8f1 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-02-04 10:57:30 +08:00
ca782d0aff feat: 竞赛主页中文 2026-02-04 10:57:25 +08:00
X1627315083
3dfb607b91 Merge remote-tracking branch 'origin/dev_vite' into StableVersion 2026-02-03 15:29:37 +08:00
X1627315083
981b4dad5c fix 2026-02-03 15:29:07 +08:00
X1627315083
181e6a87b8 Merge remote-tracking branch 'origin/dev_vite' into StableVersion 2026-02-03 10:46:09 +08:00
287825b4bf Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-02-02 17:18:17 +08:00
1ffc303721 feat: 竞赛页面AiDA入口 2026-02-02 17:00:32 +08:00
李志鹏
bdf1bb2669 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-02-02 16:52:11 +08:00
李志鹏
1fe79ffcf9 fix 2026-02-02 16:52:10 +08:00
758f63615a style: 字体大小 2026-02-02 16:13:01 +08:00
20145742c5 style: timeline布局修改 2026-02-02 15:48:50 +08:00
8ec9b1bcea Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-02-02 14:09:43 +08:00
a9cb6e16e9 style: select样式 2026-02-02 14:09:28 +08:00
X1627315083
5690fc6c5b fix 2026-02-02 13:55:12 +08:00
X1627315083
9db6a589f0 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-02-02 13:48:09 +08:00
X1627315083
896490e57b fix 2026-02-02 13:48:07 +08:00
fca04ba44b Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-02-02 13:41:56 +08:00
25e4fc06c6 style: 样式修改 2026-02-02 13:41:51 +08:00
X1627315083
4bd7740753 修复detail添加印花sort值设置不对 2026-02-02 13:30:43 +08:00
X1627315083
c428bfd93b Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-02-02 11:48:12 +08:00
X1627315083
2a29c6b2cc fix 2026-02-02 11:48:10 +08:00
李志鹏
e9d7203804 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-02-02 11:33:38 +08:00
李志鹏
5d7cec520b 平铺默认参数 2026-02-02 11:33:36 +08:00
X1627315083
fe72df0c07 fix 2026-02-02 11:30:26 +08:00
X1627315083
7c04332290 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-02-02 11:08:22 +08:00
X1627315083
73d912d3cd detail设置印花顺序相关问题 2026-02-02 11:08:20 +08:00
李志鹏
b320294764 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-02-02 10:37:14 +08:00
李志鹏
4913d02c93 fix 2026-02-02 10:37:13 +08:00
X1627315083
56916c8d10 fix 2026-02-02 10:31:37 +08:00
李志鹏
393a06eceb Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-02-02 09:47:13 +08:00
李志鹏
fdb6a87ab4 添加印花元素排序priority 2026-02-02 09:47:10 +08:00
6d868c7c7a Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-01-30 17:21:15 +08:00
89a89ea5ef style: 竞赛主页样式修改 2026-01-30 17:21:11 +08:00
李志鹏
811e179889 fix 2026-01-30 14:52:10 +08:00
李志鹏
0e0eed2566 fix 2026-01-30 14:12:17 +08:00
李志鹏
8588c74ffd 图层可以持续粘贴 2026-01-30 13:54:21 +08:00
李志鹏
62e7f34c98 画布问题更改 2026-01-30 13:47:38 +08:00
X1627315083
012f0ef1b5 修复画布打开发布后仍然可以对画布里面复制内容 2026-01-29 16:55:38 +08:00
X1627315083
2d5d1b7a5e 删除衣服时候imgDom要随着变化 2026-01-29 09:55:15 +08:00
X1627315083
f6556ec9a9 fix 2026-01-28 17:10:58 +08:00
53 changed files with 7929 additions and 6326 deletions

33
.prettierrc.js Normal file
View File

@@ -0,0 +1,33 @@
/** @type {import('prettier').Config} */
module.exports = {
// 打印宽度
printWidth: 100,
// 使用 4 空格缩进
tabWidth: 4,
// 使用 4 空格缩进,不使用制表符
useTabs: true,
// 行尾使用 LF (Unix 风格)
endOfLine: 'lf',
// 语句末尾使用分号
semi: false,
// 使用单引号
singleQuote: false,
// 对象和数组末尾不添加尾随逗号
trailingComma: 'none',
// JSX 引号使用单引号
jsxSingleQuote: false,
// 括号内侧空格
bracketSpacing: true,
// JSX 标签不换行
bracketSameLine: false,
// 箭头函数参数始终使用括号
arrowParens: 'always',
// HTML、Vue、Angular 和 Markdown 使用 LF
htmlWhitespaceSensitivity: 'css',
// Vue 文件脚本和样式缩进
vueIndentScriptAndStyle: false,
// 行注释位置在注释上方,不加空格
proseWrap: 'preserve',
// 根据文件类型自动推断
embeddedLanguageFormatting: 'auto',
};

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

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.

Before

Width:  |  Height:  |  Size: 252 B

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 MiB

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

@@ -2,92 +2,128 @@
"eventsList": [ "eventsList": [
{ {
"id": 1, "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", "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" "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": "Click the “View Details” button 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":[
{ {
"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": [
}
]
},{
"paragraph":[
{ {
"text":"<b>⚠ATTENTION❗❗</b>" "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":"1. Add the tag in the work description #AiDAworkshop_2024"
},{
"text":"2. One winner only"
}
]
},{
"paragraph":[
{
"text":"<b>🤩Code-Create will provide (Terms and conditions apply):</b>"
}
]
},{
"paragraph":[
{
"text":"✅Round-trip transportation fee (only within China)"
}
]
},{
"paragraph":[
{
"text":"✅One night accommodation fee"
}
]
},{
"paragraph":[
{
"text":"⌛Deadline: October 31, 2024"
} }
] ]
} }
] ]
}, },
{ {
"id":2, "id": 1,
"title":"AiDA X SFT AI Fashion Award 2024", "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",
"textList": [
{
"paragraph": [
{
"text": "🎨AiDA Workshop!"
}
]
},
{
"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": "<b>⚠ATTENTION❗❗</b>"
}
]
},
{
"paragraph": [
{
"text": "1. Add the tag in the work description #AiDAworkshop_2024"
},
{
"text": "2. One winner only"
}
]
},
{
"paragraph": [
{
"text": "<b>🤩Code-Create will provide (Terms and conditions apply):</b>"
}
]
},
{
"paragraph": [
{
"text": "✅Round-trip transportation fee (only within China)"
}
]
},
{
"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", "imgUrl": "/image/events/Fashion-Award-2024.png",
"textList":[ "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": "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." "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

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

@@ -2507,3 +2507,6 @@ textarea:focus {
.justify-center { .justify-center {
justify-content: center; justify-content: center;
} }
.flex-1{
flex: 1;
}

View File

@@ -2425,3 +2425,6 @@ textarea:focus{
.justify-center { .justify-center {
justify-content: center; justify-content: center;
} }
.flex-1{
flex: 1;
}

View File

@@ -109,12 +109,19 @@ 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 offsetX = object.fill?.hasOwnProperty("offsetX") ? object.fill.offsetX : tObject.width / 2;
// const offsetY = object.fill?.hasOwnProperty("offsetY") ? object.fill.offsetY : tObject.height / 2; // const offsetY = object.fill?.hasOwnProperty("offsetY") ? object.fill.offsetY : tObject.height / 2;
const patternTransform = object.fill?.hasOwnProperty("patternTransform") ? object.fill.patternTransform : createPatternTransform(0.3, 0); 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 scale = getTransformScaleAngle(patternTransform).scale;
const offsetX = tObject.width / 2 - img.width * scale / 2; const offsetX = tWidth / 2 - img.width * scale / 2;
const offsetY = tObject.height / 2 - img.height * 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,
@@ -146,10 +153,10 @@ 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,
}); });
@@ -192,7 +199,7 @@ export class FillRepeatCommand extends Command {
// 复制原对象的属性 // 复制原对象的属性
copyObjectProperties(object) { copyObjectProperties(object) {
return{ return {
id: object.id, id: object.id,
layerId: object.layerId, layerId: object.layerId,
layerName: object.layerName, layerName: object.layerName,

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

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,6 @@ import {
isGroupLayer, isGroupLayer,
OperationType, OperationType,
OperationTypes, OperationTypes,
findLayer,
createLayer, createLayer,
LayerType, LayerType,
SpecialLayerId, SpecialLayerId,
@@ -20,7 +19,6 @@ import { AnimationManager } from "./animation/AnimationManager";
import { createCanvas } from "../utils/canvasFactory"; import { createCanvas } from "../utils/canvasFactory";
import { CanvasEventManager } from "./events/CanvasEventManager"; import { CanvasEventManager } from "./events/CanvasEventManager";
import CanvasConfig from "../config/canvasConfig"; import CanvasConfig from "../config/canvasConfig";
import { RedGreenModeManager } from "./RedGreenModeManager";
import { EraserStateManager } from "./EraserStateManager"; import { EraserStateManager } from "./EraserStateManager";
import { import {
deepClone, deepClone,
@@ -870,9 +868,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;
} }
@@ -1149,7 +1147,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
@@ -1160,8 +1158,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",
@@ -1173,7 +1172,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,
@@ -1239,8 +1238,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};
} }
@@ -1336,7 +1335,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
} }
} }
loadJSON(json, calllBack) { loadJSON(json, calllBack) {
this.canvas.loading.value = true;
// 确保传入的json是字符串格式 // 确保传入的json是字符串格式
if (typeof json === "object") { if (typeof json === "object") {
json = JSON.stringify(json); json = JSON.stringify(json);
@@ -1467,9 +1466,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) {
@@ -1485,6 +1487,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
});
} }
@@ -1492,25 +1505,15 @@ 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;
let resolve = ()=>{}; let resolve = ()=>{};
this.awaitCanvasRun = ()=>(new Promise((v) => resolve = v)) this.awaitCanvasRun = ()=>(new Promise((v) => resolve = v))
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));
@@ -1518,11 +1521,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 = [];// 平铺图层
@@ -1540,14 +1547,13 @@ 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();
console.log("==========创建其他图层成功"); console.log("==========创建其他图层成功");
resolve(); resolve();
this.awaitCanvasRun = null; this.awaitCanvasRun = null;
this.canvas.loading.value = false;
} }
// 设置画布对象的裁剪信息 // 设置画布对象的裁剪信息
@@ -1671,7 +1677,7 @@ 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
@@ -1697,7 +1703,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,
@@ -1708,7 +1714,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);
}; };
@@ -1793,7 +1800,7 @@ 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,
@@ -1805,6 +1812,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
blendMode: blendMode, 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);
}; };
@@ -1821,6 +1829,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);
// 插入组图层 // 插入组图层

View File

@@ -1580,7 +1580,7 @@ export class LayerManager {
/** /**
* 排序图层,确保图层顺序: 普通图层 > 固定图层 > 背景图层 * 排序图层,确保图层顺序: 普通图层 > 固定图层 > 背景图层
*/ */
sortLayers() { async sortLayers() {
// 对图层进行排序:背景图层在最底层(数组最后),固定图层在中间 // 对图层进行排序:背景图层在最底层(数组最后),固定图层在中间
this.layers.value.sort((a, b) => { this.layers.value.sort((a, b) => {
// 如果a是背景图层它应该排在后面最底层 // 如果a是背景图层它应该排在后面最底层
@@ -1604,17 +1604,17 @@ export class LayerManager {
}); });
// 更新画布对象顺序 // 更新画布对象顺序
this._rearrangeObjects(); await this._rearrangeObjects();
} }
/** /**
* 重新排列画布上的对象以匹配图层顺序 * 重新排列画布上的对象以匹配图层顺序
* @private * @private
*/ */
_rearrangeObjects() { async _rearrangeObjects() {
if (this.layerSort) { if (this.layerSort) {
// 使用LayerSort的高级排序 // 使用LayerSort的高级排序
this.layerSort.rearrangeObjects(); await this.layerSort.rearrangeObjects();
return; return;
} }
@@ -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; // 矩形对象
@@ -90,16 +90,17 @@ export class PartManager {
if (toolId === OperationType.PART_ERASER) { if (toolId === OperationType.PART_ERASER) {
this.setEraserTool(); this.setEraserTool();
} else if (toolId === OperationType.PART || toolId === OperationType.PART_RECTANGLE) {
this.clearPointData();
this.resetPartObject();
}
if (toolId === OperationType.PART_ERASER || toolId === OperationType.PART_BRUSH) {
if (this.pointList.length > 0) {
this.clearPointData();
this.resetPartObject();
}
} }
// else if (toolId === OperationType.PART || toolId === OperationType.PART_RECTANGLE) {
// this.clearPointData();
// this.resetPartObject();
// }
// if (toolId === OperationType.PART_ERASER || toolId === OperationType.PART_BRUSH) {
// if (this.pointList.length > 0) {
// this.clearPointData();
// this.resetPartObject();
// }
// }
// 如果从非选区工具切换到选区工具,初始化事件 // 如果从非选区工具切换到选区工具,初始化事件
if (!wasActive && this.isActive) { if (!wasActive && this.isActive) {
@@ -380,7 +381,8 @@ export class PartManager {
box: [...this.pointList], box: [...this.pointList],
}); });
const image = await this.loadImageToObject(url); const image = await this.loadImageToObject(url);
const canvas = getObjectAlphaToCanvas(image, null, 0, this.rgba); const data = this.partCanvas?.getContext("2d")?.getImageData(0, 0, this.partCanvas.width, this.partCanvas.height);
const canvas = getObjectAlphaToCanvas(image, data, 0, this.rgba, !!data);
this.partDrawCommand(canvas); this.partDrawCommand(canvas);
} }
/** 获取分隔后图片 */ /** 获取分隔后图片 */

View File

@@ -30,7 +30,8 @@ export class LayerSort {
if (canvasObjects.length === 0) return; if (canvasObjects.length === 0) return;
// 使用画布渲染优化 // 使用画布渲染优化
await optimizeCanvasRendering(this.canvas, () => { await new Promise((resolve) => {
optimizeCanvasRendering(this.canvas, () => {
// 计算每个对象应该在的 z-index 位置 // 计算每个对象应该在的 z-index 位置
const objectZIndexMap = this.calculateObjectZIndexes(); const objectZIndexMap = this.calculateObjectZIndexes();
@@ -51,6 +52,8 @@ export class LayerSort {
this.canvas.moveTo(item.object, index); this.canvas.moveTo(item.object, index);
} }
}); });
resolve();
});
}); });
} }

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

@@ -65,9 +65,10 @@ export async function restoreFabricObject(serializedObject, canvas) {
* @param {ImageData} revData - 相反的ImageData白通道的相同位置是否为透明revData为白色为透明黑色为不透明 * @param {ImageData} revData - 相反的ImageData白通道的相同位置是否为透明revData为白色为透明黑色为不透明
* @param {number} diff - 差值,默认 25 * @param {number} diff - 差值,默认 25
* @param {Object} rgba - 自定义 rgba 值,默认 { r: 255, g: 255, b: 255, a: 255 } * @param {Object} rgba - 自定义 rgba 值,默认 { r: 255, g: 255, b: 255, a: 255 }
* @param {boolean} isMerge - 是否合并true=合并revDatafalse=反转revData
* @returns {HTMLCanvasElement|null} 包含黑白通道的画布,或 null 如果失败 * @returns {HTMLCanvasElement|null} 包含黑白通道的画布,或 null 如果失败
*/ */
export function getObjectAlphaToCanvas(object, revData, diff = 30, rgba = { r: 255, g: 255, b: 255, a: 255 }) { export function getObjectAlphaToCanvas(object, revData, diff = 30, rgba = { r: 255, g: 255, b: 255, a: 255 }, isMerge = false) {
const image = object.getElement(); const image = object.getElement();
if (image.nodeName !== "IMG" && image.nodeName !== "CANVAS") { if (image.nodeName !== "IMG" && image.nodeName !== "CANVAS") {
console.warn("对象不是图片"); console.warn("对象不是图片");
@@ -93,18 +94,20 @@ export function getObjectAlphaToCanvas(object, revData, diff = 30, rgba = { r: 2
const revG = revData?.data[i + 1] || 0; const revG = revData?.data[i + 1] || 0;
const revB = revData?.data[i + 2] || 0; const revB = revData?.data[i + 2] || 0;
const revA = revData?.data[i + 3] || 0; const revA = revData?.data[i + 3] || 0;
let isHave = false;
if (r || g || b || a) { if (r || g || b || a) {
if (revR > diff || revG > diff || revB > diff || revA > diff) { if (revR > diff || revG > diff || revB > diff || revA > diff) {
data.data[i + 0] = 0; isHave = false;
data.data[i + 1] = 0;
data.data[i + 2] = 0;
data.data[i + 3] = 0;
} else { } else {
isHave = true;
}
}
if (isMerge && (revR || revG || revB || revA)) isHave = true;
if (isHave) {
data.data[i + 0] = rgba.r; data.data[i + 0] = rgba.r;
data.data[i + 1] = rgba.g; data.data[i + 1] = rgba.g;
data.data[i + 2] = rgba.b; data.data[i + 2] = rgba.b;
data.data[i + 3] = rgba.a; data.data[i + 3] = rgba.a;
}
} else { } else {
data.data[i + 0] = 0; data.data[i + 0] = 0;
data.data[i + 1] = 0; data.data[i + 1] = 0;

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

@@ -333,6 +333,7 @@
]); ]);
const canvasLoadJsonSuccess = () => { const canvasLoadJsonSuccess = () => {
console.log("画布加载JSON成功"); console.log("画布加载JSON成功");
return;
canvasEditor.value?.updateOtherLayers({ canvasEditor.value?.updateOtherLayers({
color: { rgba: { r: 255, g: 0, b: 0, a: 1 } }, color: { rgba: { r: 255, g: 0, b: 0, a: 1 } },
printObject: { printObject: {
@@ -345,6 +346,7 @@
location: [800, 600], location: [800, 600],
scale: [1, 1], scale: [1, 1],
angle: 0, angle: 0,
priority: 1,
object: { object: {
top: 300, top: 300,
left: 400, left: 400,
@@ -354,20 +356,21 @@
angle: 0, 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 +379,7 @@
// location: [700, 400], // location: [700, 400],
// scale: [0.1, 0.133], // scale: [0.1, 0.133],
// angle: 0, // angle: 0,
// priority: 3,
// }, // },
], ],
}, },

View File

@@ -500,7 +500,7 @@ export default defineComponent({
} }
const submit = async ()=>{ const submit = async ()=>{
detailData.loadingShow = true detailData.loadingShow = true
if(detailData.isEditPattern.value !== 'canvasEditor'){ if(detailData.isEditPattern.value !== 'canvasEditor' && detailDom.canvasBox){
if(detailDom.detailRight?.privewDetail)await (detailDom.detailRight as any).privewDetail() if(detailDom.detailRight?.privewDetail)await (detailDom.detailRight as any).privewDetail()
let otherData = await updateOtherLayers('single') let otherData = await updateOtherLayers('single')
await detailDom.canvasBox.updateOtherLayers(otherData) await detailDom.canvasBox.updateOtherLayers(otherData)
@@ -759,7 +759,6 @@ export default defineComponent({
const uploadSelectDetail = async ()=>{//更新选中的detail const uploadSelectDetail = async ()=>{//更新选中的detail
// await detailDom.canvasBox.saveCanvas() // await detailDom.canvasBox.saveCanvas()
const allInfo = await (detailDom.canvasBox as any).getCanvasElement() const allInfo = await (detailDom.canvasBox as any).getCanvasElement()
console.log(allInfo)
let color:any = {} let color:any = {}
if(allInfo.color?.color?.rgba || allInfo.color?.color?.gradient){ if(allInfo.color?.color?.rgba || allInfo.color?.color?.gradient){
let canvasColor = allInfo.color.color; let canvasColor = allInfo.color.color;

View File

@@ -12,7 +12,6 @@
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"
@@ -52,9 +51,9 @@
</div> </div>
</div> </div>
<div class="mark_loading" v-show="isShowMark"> <!-- <div class="mark_loading" v-show="isShowMark">
<a-spin size="large" /> <a-spin size="large" />
</div> </div> -->
</div> </div>
</template> </template>

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,29 +233,47 @@ 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:editPrintElementData.stateOverallSingle == 'single'?scale:[1,1],
globalCompositeOperation:'', globalCompositeOperation:'',
} }
getItemPosition(item) getItemPosition(item)
@@ -283,10 +300,10 @@ export default defineComponent({
// location = [item.pattern.style.left,item.pattern.style.top] // location = [item.pattern.style.left,item.pattern.style.top]
} }
let value ={ let value ={
angle : item.pattern.transform.rotateZ, angle:0,
// 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,
@@ -295,16 +312,22 @@ export default defineComponent({
ifSingle:!!item.ifSingle, ifSingle:!!item.ifSingle,
globalCompositeOperation:'', globalCompositeOperation:'',
} }
if(item.object)value.object = item.object if(item.object)value.object = item.object;
value.angle = value.ifSingle?item.pattern.transform.rotateZ:item.angle
return value return value
} }
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 +359,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 = item.scale || [1,1]
} }
let pattern = { let pattern = {
centers:{left:0,top:0}, centers:{left:0,top:0},
@@ -357,7 +379,6 @@ export default defineComponent({
}, },
designOpenrtionBtn:false designOpenrtionBtn:false
} }
editPrintElementData.printZIndex++
item.pattern = pattern item.pattern = pattern
if(item.object){ if(item.object){
@@ -400,7 +421,8 @@ export default defineComponent({
} }
} }
} }
const setPosition = ()=>{ const setPosition = async ()=>{
await new Promise<void>((resolve, reject) => {
nextTick(()=>{ nextTick(()=>{
let img = new Image let img = new Image
img.onload = ()=>{ img.onload = ()=>{
@@ -436,15 +458,12 @@ export default defineComponent({
}) })
setItemPosition() setItemPosition()
} }
// if(props.type == 'print'){ resolve('')
// editPrintElementData.overallSingle = state
// }
} }
// undividedLayer img.src = editPrintElementData.selectDetail.path
//计算宽高使用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
@@ -518,7 +537,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,
@@ -652,7 +670,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)
@@ -804,7 +821,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();
} }
} }
@@ -835,6 +853,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)
@@ -855,7 +874,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) => {
@@ -866,7 +884,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,
}); });
} }
@@ -898,6 +917,7 @@ export default defineComponent({
} }
const inputFillAngle = (angle:any)=>{ const inputFillAngle = (angle:any)=>{
let arr = editPrintElementData.printStyleList[props.type].overall let arr = editPrintElementData.printStyleList[props.type].overall
console.log(angle)
arr[editPrintElementData.imgDomIndex].angle = angle arr[editPrintElementData.imgDomIndex].angle = angle
editPrintElementDom.pingpuRef.updataList([ editPrintElementDom.pingpuRef.updataList([
{ {

View File

@@ -25,7 +25,7 @@
<div class="repeat-setting-item"> <div class="repeat-setting-item">
<span class="label">Gap X</span> <span class="label">Gap X</span>
<slider <slider
:min="0" :min="1"
:max="1000" :max="1000"
:step="1" :step="1"
is-input is-input
@@ -39,7 +39,7 @@
<div class="repeat-setting-item"> <div class="repeat-setting-item">
<span class="label">Gap Y</span> <span class="label">Gap Y</span>
<slider <slider
:min="0" :min="1"
:max="1000" :max="1000"
:step="1" :step="1"
is-input is-input
@@ -84,7 +84,7 @@
const scale = computed(() => { const scale = computed(() => {
// let scaleValue = props.object?.scale/10; // let scaleValue = props.object?.scale/10;
// return props.object?.scale/10; // return props.object?.scale/10;
return props.object?.scale[0] * 100; return (props.object?.scale[0] * 100).toFixed(0);
}); });
const scalePrint = computed(() => { const scalePrint = computed(() => {
let index = sketchWH.value[0] > sketchWH.value[1]?0:1; let index = sketchWH.value[0] > sketchWH.value[1]?0:1;

File diff suppressed because one or more lines are too long

View File

@@ -1,108 +1,138 @@
<template> <template>
<div class="eventsDetail_page" :class="{active:isScroll}"> <div class="eventsDetail_page" :class="{ active: isScroll }">
<div class="eventsDetail_title "> <div class="eventsDetail_title">
<div class="modal_title_text" @click="setBack"> <div class="modal_title_text" @click="setBack">
<i class="fi fi-sr-left"></i> <i class="fi fi-sr-left"></i>
<div class="eventsDetail_title_text">{{ $t('event.back') }}</div> <div class="eventsDetail_title_text">{{ $t("event.back") }}</div>
</div> </div>
</div> </div>
<div class="eventsDetail_content"> <div class="eventsDetail_content">
<div class="eventsDetail_content_left"> <div class="eventsDetail_content_left">
<fullScreenImg :src="eventsDetail.imgUrl" width="100%" :center="true"></fullScreenImg> <fullScreenImg
:src="eventsDetail.imgUrl"
width="100%"
:center="true"
></fullScreenImg>
</div> </div>
<div class="eventsDetail_content_right"> <div class="eventsDetail_content_right">
<div class="modal_title_text"> <div class="modal_title_text modal_title_text-header flex space-between">
<div>{{ eventsDetail.title }}</div> <div>{{ eventsDetail.title }}</div>
<div class="detail-btn" v-if="eventsDetail.id === 3" @click="openDetail">
{{ $t("event.detail") }}
</div> </div>
<div class="modal_title_text" v-for="item in eventsDetail.textList"> </div>
<div class="modal_title_text content" v-for="item in eventsDetail.textList">
<div class="eventsDetail_content_right_btn_box"> <div class="eventsDetail_content_right_btn_box">
<div class="eventsDetail_content_right_btn" v-for="buttonItem,buttonIndex in item?.button" @click="openButton(buttonItem,buttonIndex)"> <div
<div v-show="!loadingShow[buttonIndex]" class="started_btn">{{ buttonItem.text }}</div> class="eventsDetail_content_right_btn"
<div v-show="loadingShow[buttonIndex]" class="started_btn"><i class="fi fi-br-loading"></i></div> v-for="(buttonItem, buttonIndex) in item?.button"
@click="openButton(buttonItem, buttonIndex)"
>
<div v-show="!loadingShow[buttonIndex]" class="started_btn">
{{ buttonItem.text }}
</div> </div>
</div> <div v-show="loadingShow[buttonIndex]" class="started_btn">
<div class="modal_title_text_intro" v-for="introItem in item?.paragraph" :class="{active:introItem.display == 'flex'}" v-detailText="introItem.text"> <i class="fi fi-br-loading"></i>
</div> </div>
</div> </div>
</div> </div>
<div
class="modal_title_text_intro"
v-for="introItem in item?.paragraph"
:class="{ active: introItem.display == 'flex' }"
v-detailText="introItem.text"
></div>
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { LoadingOutlined } from "@ant-design/icons-vue"; import { LoadingOutlined } from "@ant-design/icons-vue"
import { defineComponent,h ,toRefs,ref,reactive,onMounted,nextTick,provide,computed} from 'vue' import {
defineComponent,
h,
toRefs,
ref,
reactive,
onMounted,
nextTick,
provide,
computed
} from "vue"
// import RobotAssist from "@/component/HomePage/RobotAssist.vue"; // import RobotAssist from "@/component/HomePage/RobotAssist.vue";
import { Https } from "@/tool/https"; import { Https } from "@/tool/https"
import { message, Upload, Modal } from "ant-design-vue"; import { message, Upload, Modal } from "ant-design-vue"
import fullScreenImg from '@/component/HomePage/fullScreenImg.vue' import fullScreenImg from "@/component/HomePage/fullScreenImg.vue"
import { useRouter } from 'vue-router'; import { useRouter } from "vue-router"
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n"
import generalMenu from "@/component/HomePage/generalMenu.vue"; import generalMenu from "@/component/HomePage/generalMenu.vue"
import eventData from "@/assets/json/events.json"; import eventData from "@/assets/json/events.json"
import eventDataCn from "@/assets/json/events_cn.json"; import eventDataCn from "@/assets/json/events_cn.json"
import { useStore } from "vuex"; import { useStore } from "vuex"
export default defineComponent({ export default defineComponent({
components: { components: {
generalMenu, generalMenu,
fullScreenImg, fullScreenImg
},
props:{
isScroll:{
type:Boolean,
default:true,
}, },
props: {
isScroll: {
type: Boolean,
default: true
}
}, },
setup() { setup() {
const router = useRouter(); const router = useRouter()
const store = useStore(); const store = useStore()
let filter:any = reactive({ let filter: any = reactive({
eventsDetail: { eventsDetail: {},
getListDate: {
getLikePortfolio: 0,
getMyPortfolio: 0,
page: 1,
size: 10
}, },
getListDate:{ isShowMark: false,
"getLikePortfolio": 0, isNoData: false, //如果数据为空就不加载
"getMyPortfolio": 0, loadingShow: {}
page:1,
size:10,
},
isShowMark:false,
isNoData:false,//如果数据为空就不加载
loadingShow:{},
}) })
let likeFile = (item:any,type:string) => { let likeFile = (item: any, type: string) => {}
} let setBack = () => {
let setBack = ()=>{ router.go(-1)
router.go(-1);
// router.push('/home/events') // router.push('/home/events')
} }
let openButton = (data:any,index:number)=>{ let openButton = (data: any, index: number) => {
if(filter.loadingShow[index]){ if (filter.loadingShow[index]) {
return return
} }
filter.loadingShow[index] = true filter.loadingShow[index] = true
Https.axiosGet(data.https).then( Https.axiosGet(data.https)
(rv: any) => { .then((rv: any) => {
if(rv){ if (rv) {
message.success(data.success) message.success(data.success)
filter.loadingShow[index] = false filter.loadingShow[index] = false
} }
} })
).catch(res=>{ .catch((res) => {
filter.loadingShow[index] = false filter.loadingShow[index] = false
}); })
} }
onMounted (()=>{ const openDetail = () => {
const { t, locale } = useI18n(); // window.open("https://aida-global-design-awards.com.hk", "_blank")
const currentLocale = locale.value; router.push("/award/index")
let eventLangData:any }
if(currentLocale == 'ENGLISH'){ onMounted(() => {
const { t, locale } = useI18n()
const currentLocale = locale.value
let eventLangData: any
if (currentLocale == "ENGLISH") {
eventLangData = eventData eventLangData = eventData
}else{ } else {
eventLangData = eventDataCn eventLangData = eventDataCn
} }
eventLangData.eventsItem.forEach((item:any)=>{ eventLangData.eventsItem.forEach((item: any) => {
if(item.id == router.currentRoute.value.query.eventId){ if (item.id == router.currentRoute.value.query.eventId) {
filter.eventsDetail = item; filter.eventsDetail = item
} }
}) })
}) })
@@ -111,18 +141,17 @@ export default defineComponent({
likeFile, likeFile,
setBack, setBack,
openButton, openButton,
openDetail
} }
}, },
directives:{ directives: {
detailText:{ detailText: {
mounted (el,binding) { mounted(el, binding) {
el.innerHTML = binding.value el.innerHTML = binding.value
} }
} }
}, },
async mounted(){ async mounted() {}
},
}) })
</script> </script>
<style lang="less"> <style lang="less">
@@ -132,11 +161,11 @@ export default defineComponent({
padding: 0 6rem; padding: 0 6rem;
padding-top: 5rem; padding-top: 5rem;
&.active{ &.active {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
.eventsDetail_content{ .eventsDetail_content {
overflow-y: auto; overflow-y: auto;
width: 100%; width: 100%;
} }
@@ -146,7 +175,7 @@ export default defineComponent({
min-height: auto; min-height: auto;
padding-bottom: 10rem; padding-bottom: 10rem;
} }
.eventsDetail_title{ .eventsDetail_title {
display: flex; display: flex;
padding: 2rem 0rem; padding: 2rem 0rem;
align-items: center; align-items: center;
@@ -154,21 +183,21 @@ export default defineComponent({
top: 0; top: 0;
z-index: 222; z-index: 222;
background: #fff; background: #fff;
.modal_title_text{ .modal_title_text {
cursor: pointer; cursor: pointer;
display: flex; display: flex;
margin-bottom: 0; margin-bottom: 0;
} }
.modal_title_text:hover .eventsDetail_title_text{ .modal_title_text:hover .eventsDetail_title_text {
text-decoration: underline; text-decoration: underline;
} }
i{ i {
display: flex; display: flex;
align-items: center; align-items: center;
margin-right: 1rem; margin-right: 1rem;
} }
} }
.eventsDetail_content{ .eventsDetail_content {
border-top: 1px solid #f0f0f0; border-top: 1px solid #f0f0f0;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -176,59 +205,69 @@ export default defineComponent({
@media (max-width: 768px) { @media (max-width: 768px) {
flex-direction: column; flex-direction: column;
} }
.eventsDetail_content_left,.eventsDetail_content_right{ .eventsDetail_content_left,
.eventsDetail_content_right {
width: 50%; width: 50%;
@media (max-width: 768px) { @media (max-width: 768px) {
width: 100%; width: 100%;
} }
} }
.eventsDetail_content_left{ .eventsDetail_content_left {
width: 40%; width: 40%;
max-height: 60rem; max-height: 60rem;
@media (max-width: 768px) { @media (max-width: 768px) {
width: 100%; width: 100%;
} }
.ant-image{ .ant-image {
// height: auto; // height: auto;
height: 100%; height: 100%;
} }
.eventsDetail_content_left_img{ .eventsDetail_content_left_img {
width: 100%; width: 100%;
cursor: zoom-in; cursor: zoom-in;
} }
} }
.eventsDetail_content_right{ .eventsDetail_content_right {
.modal_title_text{ .modal_title_text {
letter-spacing: .4rem; letter-spacing: 0.4rem;
font-weight: 600; font-weight: 600;
.modal_title_text_intro{ &-header {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
justify-content: space-between;
gap: 1rem;
> div:first-child {
flex: 1;
min-width: 0;
}
}
.modal_title_text_intro {
display: block; display: block;
&.active{ &.active {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
li{ li {
width: 48%; width: 48%;
} }
em{ em {
// font-family: auto; // font-family: auto;
} }
a{ a {
display: inline; display: inline;
} }
} }
.eventsDetail_content_right_btn_box{ .eventsDetail_content_right_btn_box {
display: flex; display: flex;
justify-content: space-evenly; justify-content: space-evenly;
.eventsDetail_content_right_btn{ .eventsDetail_content_right_btn {
} }
} }
} }
.modal_title_text:last-child{ .modal_title_text:last-child {
} }
.modal_title_text:last-child::after{ .modal_title_text:last-child::after {
content: ""; content: "";
display: block; display: block;
border-top: 3px solid; border-top: 3px solid;
@@ -237,4 +276,17 @@ export default defineComponent({
} }
} }
} }
.detail-btn {
// width: 11rem;
padding: 0 1.4rem;
height: 4rem;
line-height: 4rem;
text-align: center;
color: #fff;
border-radius: 2rem;
background-color: #000;
font-size: 1.4rem;
white-space: nowrap;
cursor: pointer;
}
</style> </style>

View File

@@ -231,7 +231,8 @@ export default {
colorboard: '调色板', colorboard: '调色板',
sketchboard: '线稿板', sketchboard: '线稿板',
mannequins: '人体模型', mannequins: '人体模型',
masnnequinHint: '您使用的模特与当前的衣服不匹配,这将导致生成的模型不使用所选的衣服', masnnequinHint:
'您使用的模特与当前的衣服不匹配,这将导致生成的模型不使用所选的衣服',
FinalizeCollection: '完成系列', FinalizeCollection: '完成系列',
jsContent1: '您必须选择一种或多种颜色进行下一步。', jsContent1: '您必须选择一种或多种颜色进行下一步。',
jsContent2: '您必须选择一种或多种颜色进行下一步。', jsContent2: '您必须选择一种或多种颜色进行下一步。',
@@ -978,17 +979,13 @@ export default {
subscriptionRenewal: '没有自动续订的订阅计划.' subscriptionRenewal: '没有自动续订的订阅计划.'
}, },
guide: { guide: {
guide1: guide1: '在<strong>工作空间</strong>中,您可以个性化您的设计设置,包括选择适用于男装或女装的设计,以及选择用于创作的人体模型。',
'在<strong>工作空间</strong>中,您可以个性化您的设计设置,包括选择适用于男装或女装的设计,以及选择用于创作的人体模型。',
guide2: '选择您要设计的服装性别。', guide2: '选择您要设计的服装性别。',
guide3: '在此更改人体模型。', guide3: '在此更改人体模型。',
guide4: guide4: '您目前可以从我们的系统库中选择人体模型。稍后,您还可以在注册自己的人体模型后从用户库中进行选择。',
'您目前可以从我们的系统库中选择人体模型。稍后,您还可以在注册自己的人体模型后从用户库中进行选择。',
guide5: '在这里开始您的创意之旅。 ', guide5: '在这里开始您的创意之旅。 ',
guide6: guide6: '对于情绪板、印花或服装,我们提供三种不同的图片添加方法。第一种选择是<strong>上传</strong>,允许您直接从本地设备上传。',
'对于情绪板、印花或服装,我们提供三种不同的图片添加方法。第一种选择是<strong>上传</strong>,允许您直接从本地设备上传。', guide7: '第二种方法是从您的<strong>收藏</strong>中选择。<br> 您可能会注意到您的库页面目前是空的;不必担心。您上传的所有图像都将自动添加到您的库中。将来,您无需每次上传,只需从您的库中选择即可。',
guide7:
'第二种方法是从您的<strong>收藏</strong>中选择。<br> 您可能会注意到您的库页面目前是空的;不必担心。您上传的所有图像都将自动添加到您的库中。将来,您无需每次上传,只需从您的库中选择即可。',
guide8: '第三种方法是使用最新的图像生成技术<strong>生成</strong>图像。', guide8: '第三种方法是使用最新的图像生成技术<strong>生成</strong>图像。',
guide9: '输入捕捉您希望表达的情绪的关键词,然后单击<strong>生成</strong>按钮。', guide9: '输入捕捉您希望表达的情绪的关键词,然后单击<strong>生成</strong>按钮。',
guide10: '为您的心情板选择两个图像。', guide10: '为您的心情板选择两个图像。',
@@ -1150,7 +1147,7 @@ export default {
CanvasTitle: { CanvasTitle: {
ModifySketch: '修改草图', ModifySketch: '修改草图',
ModifyItem: '修改单品', ModifyItem: '修改单品',
RedGreen: '编辑前片后片', RedGreen: '编辑前片后片'
}, },
Canvas: { Canvas: {
Canvas: '画布', Canvas: '画布',
@@ -1508,7 +1505,7 @@ export default {
PointSelection: '点选', PointSelection: '点选',
MarqueeSelection: '框选', MarqueeSelection: '框选',
BrushSelection: '画笔', BrushSelection: '画笔',
Erase: '擦除', Erase: '擦除'
}, },
speedList: { speedList: {
High: '高级', High: '高级',
@@ -1532,7 +1529,8 @@ export default {
LiquefactionTool: '液化工具' LiquefactionTool: '液化工具'
}, },
event: { event: {
back: '返回' back: '返回',
detail:'查看详情'
}, },
admin: { admin: {
allUser: '所有用户', allUser: '所有用户',
@@ -1606,7 +1604,7 @@ export default {
Cancel: '取消', Cancel: '取消',
SelectPlan: '选择计划', SelectPlan: '选择计划',
AllPlan: '全部', AllPlan: '全部',
PlanStart:'订阅计划生效时间:' PlanStart: '订阅计划生效时间:'
}, },
Login: { Login: {
Login: '登录', Login: '登录',
@@ -1614,8 +1612,7 @@ export default {
ForgotPassword: '忘记密码', ForgotPassword: '忘记密码',
Welcome: '欢迎来到', Welcome: '欢迎来到',
AiDA: 'AiDA', AiDA: 'AiDA',
Slogan: Slogan: 'AiDA是一个业界首创的平台致力于赋能时装设计师融汇灵感人机协同共创原创设计。',
'AiDA是一个业界首创的平台致力于赋能时装设计师融汇灵感人机协同共创原创设计。',
LoginMethod: '使用以下方式登录:', LoginMethod: '使用以下方式登录:',
Individual: '个人账号', Individual: '个人账号',
Academic: '学术账号', Academic: '学术账号',
@@ -1625,7 +1622,7 @@ export default {
AgreePolicies: '请勾选条款、隐私政策和费用', AgreePolicies: '请勾选条款、隐私政策和费用',
PasswordConditions: '您必须满足所有密码条件才能注册', PasswordConditions: '您必须满足所有密码条件才能注册',
LoginWithGoogle: '使用谷歌账号登录', LoginWithGoogle: '使用谷歌账号登录',
LoginWithWechat: '使用微信登录', LoginWithWechat: '使用微信登录'
}, },
LoginPersonal: { LoginPersonal: {
Email: '邮箱', Email: '邮箱',
@@ -1718,5 +1715,247 @@ export default {
IncorrectEmailFormat: '请输入正确的邮箱格式', IncorrectEmailFormat: '请输入正确的邮箱格式',
CompleteVerificationCode: '请输入完整的验证码', CompleteVerificationCode: '请输入完整的验证码',
PleaseEnterYourAccountNumberOrPassword: '请输入您的账号或密码' PleaseEnterYourAccountNumberOrPassword: '请输入您的账号或密码'
},
AwardsPage: {
submitApplication: '提交申请',
applicationDeadline: '申请期限2026年7月15日',
howToApply: '申请方法',
stepByStep: '步骤指南',
step1Title: '1. 成为 AiDA 订阅用户',
step1Desc:
'所有申请者在提交时必须是\n活跃的AiDA 订阅用户。\n您可以选择按月或按年订阅。',
step2Title: '2. 通过 AiDA 设计您的作品',
step2Desc: '申请者必须仅使用AiDA\n平台完成设计作品。',
step2ListTitle: '您的作品应清楚体现以下内容:',
step2List: [
'· AiDA在创作中的应用方式',
'· 您的设计理念和创意方向',
'· AI与人类创意的融合'
],
step3Title: '3. 准备提交材料',
processVideo: '创作过程视频',
processVideoDesc: '请提供一段屏幕录制视频,展示您\n使用AiDA的创作过程。',
videoRequirements: '视频要求:',
videoFormat: '格式MP4',
videoResolution: '分辨率1080×1920px',
videoDuration: '时长最长1分钟',
videoSize: '文件大小不超过20MB',
fileName: '文件命名',
fileNameDesc: 'AiDAGlobalDesignAward\n2026_[你的名字]',
designPortfolio: '设计作品集(PDF)',
submitPdf: '提交一份包含以下内容的单一PDF文件',
requiredStructure: '',
pdfDesignTitle: '设计标题',
pdfMoodboard: '灵感板,情绪板',
pdfConcept: '概念说明',
pdfConceptDesc: '(说明如何使用AiDA进行设计创作)',
pdfRequirements: 'PDF要求',
pdfMaxPages: '最多15页',
pdfMaxSize: '最大文件大小不超过20MB',
pdfLanguage: '语言:英文,或本国语言附带英文翻译',
step4Title: '4. 决赛入围选手提交要求',
step4Subtitle: '(前20名设计师)',
step4Desc: '入围的20名决赛选手需提交实体服装以供最终评审。',
finalistPieces: '件数1件套装',
finalistBasedOn: '服装要求必须根据提交的AiDA生成设计制作',
finalistShipping: '运输说明:\n将由Code-create提供',
bloomYourCreativity: '绽放你的创造力',
themeOf2026: '赛事主题',
bloomText: {
desc1: {
regular1: '',
bold1: 'AiDA全球设计奖2026',
regular2: '是由全球领先的AI时尚解决方案提供商',
bold2: ' Code-create ',
regular3: '主办的',
bold3: '国际设计竞赛,\n',
regular4:
'旨在庆祝人工只能赋能下的未来创意。该赛事汇聚来自世界各地的设计师,\n将AI视为创意伙伴,突破传统时尚边界,探索技术与人类想象力结合的无限可能。',
bold4: '',
regular5: ''
},
desc2: {
regular1: '本届大赛以',
bold1: '"想象遇见创新,创意绽放"',
regular2:
'为主题,邀请参赛者将大胆创意转化为非凡设计,\n在 AI 辅助下实现艺术与科技的完美融合。AiDA 鼓励设计师突破常规,挑战时尚边界,\n并通过平台展示才华与全球同行、行业领袖及 AI 专家建立深度联系,共同探索未来设计的可能。'
}
},
panelOfJudges: '终审评委团',
expertise: '权威阵容',
judgesHat: {
jae: 'CodeCreate 韩国分公司总监\nBesfxxk 创意总监',
diego: 'OnTheList香港\n联合创始人兼首席执行官',
gregory: 'Gabriela Hearst\n意大利高级设计师',
vincenzo: '《南华早报》Style 杂志\n香港主编',
tim: '现代传播集团\n上海时尚总监',
desmond: '《Vogue》\n新加坡主编'
},
awardPrizes: '奖项与奖金',
recognition: '荣誉认可',
grandMoney: '5,000美元',
goldMoney: '3,000美元',
silverMoney: '1,000美元',
grandAwards: '最高奖项',
goldAwards: '金奖',
silverAwards: '银奖',
finalists: '决赛选手',
cashAward: '现金奖励',
awardCertificate: '获奖证书',
globalMediaExposure: '全球媒体曝光',
awardCertification: '获奖认证',
TravelAllowance: '差旅补贴',
selectionCriteria: '作品评选',
evaluation: '考量标准',
originality: '原创性',
originalityDesc:
'作品应体现设计师的独到视角与创新方法,展现突破常规的创意与实验性设计。',
creativity: '创造力',
creativityDesc:
'作品应展现设计师的艺术视野与卓越设计水准,体现高水平的创意表达与专业执行力。',
aidaIntegration: 'AiDA 创意整合程度',
aidaIntegrationDesc:
'作品应充分利用 AiDA 功能, 展现 AI 辅助创作在设计中的 有效应用与创新整合。',
execution: '样衣做工',
executionDesc:
'作品应具备高水平的呈现质量与精湛的技术工艺,体现专业执行力与细节把控能力。',
totalCashPrizes: '最高可达9,000美元',
totalCashPrizesLabel: '现金奖励总额',
globalMediaExpose: '全球媒体曝光',
globalMediaExposeLabel: '由国际顶级媒体平台展示​',
networkingOpportunities: '链接全球行业人脉',
networkingOpportunitiesLabel: '对接设计师与行业领军人物',
awardCeremonyHongKong: '香港颁奖盛会​',
awardCeremonyLabel: '入围者享有差旅支持',
competitionTimeline: '赛事时间表',
shapingTheFuture: '重要节点',
timelineApplicationLabel: '申请期限',
timelineDeadlineLabel: '',
timeJul15: '7月15日',
applicationDeadlineDesc: '申请截止日期及\n作品审核流程开始',
twentyFinalistsAnnounced: '20名入围者揭晓',
announcedLabel: '',
timeAug30: '8月30日',
twentyFinalistsDesc: '公布进入终评阶段的 20 名入围者',
finalistSubmission: '入围设计作品',
submissionLabel: '提交最后期限',
timeSept30: '9月30日',
finalistSubmissionDesc: '入围者上传完成的设计\n作品以进行终评',
receivingOutfits: '入围者',
fromFinalistsLabel: '提交成衣',
timeOctober: '10月',
receivingOutfitsDesc: 'AiDA 接收每位入围\n的1套实物服装',
awardCeremony: '奖项颁发仪式',
ceremonyLabel: '',
timeNov12: '11月12日',
awardCeremonyDesc: '颁奖盛典与设计师社\n群聚会 Soho House',
submissionSuccessful: '提交成功',
submissionSuccessfulDesc:
'请在 AiDA 平台内的消息中查看您提交的信息。如有需要,您可以进行修改。\n比赛的最新消息和结果将通过邮箱发送。',
deadlinePassed: '申请截止日期已过',
deadlinePassedDesc:
'AiDA 全球设计奖 2026 的作品提交已截止。\n我们不再接受新的报名。',
uploadInProgress: '上传中…',
uploadSuccess: '上传成功',
uploadFailed: '上传失败',
pdfFileTip: 'PDF文件不超过20MB',
videoFileTip: '视频文件(MP4, MOV)1080p不超过100MB',
wechatTitle: '微信公众号',
wechatDesc: '请使用微信扫描二维码'
},
AwardApply: {
// 页面主标题区域
applicationForm: '参赛表格',
emailVerification: '邮箱验证',
aidaUsersOnly: '仅限 AiDA 用户',
slogan: '绽放你的创意 • AiDA 全球设计奖 2026',
// 邮箱验证部分
emailAddress: '邮箱',
sendCode: '发送验证码',
pleaseUseRegisteredEmail: '请使用您在 AiDA 注册的邮箱',
// 个人信息部分
personalInformation: '个人信息',
tellUsAboutYourself: '自我介绍',
firstName: '名',
lastName: '姓',
gender: '性别',
occupation: '职业',
age: '年龄',
countryRegionCity: '国家或地区及城市',
phoneNumber: '电话号码',
portfolioUrl: '作品集网址或Instagram可选',
// 性别选项
male: '男',
female: '女',
other: '其他',
// 设计信息部分
designInformation: '作品信息',
shareYourCreativeVision: '分享您的创意构想',
designTitle: '作品标题',
designDescription: '设计说明',
designDescriptionPlaceholder: '请简要描述您的设计理念、灵感和创意方向...',
// 提交文件部分
submissionFiles: '作品上传',
uploadYourDesignMaterials: '上传你的设计材料',
submissionRequirements: '提交要求',
pdfRequirement: `单独PDF文件\n 作品标题、灵感板及情绪板,设计说明\n+ 4套服装设计及材料说明页数最多15页`,
rightContent: {
format: '格式:单个 PDF 文件最多15页最大 20MB',
video: `视频创作过程分辨率1080×1920 像素\n9:16 纵向比例),最长 60 秒`
},
// PDF 上传
uploadPdfTitle: '您在设计过程中如何使用 AiDA',
clickToUploadPdf: '点击选择或拖拽文件上传',
pdfFileLimit: 'PDF 文件,不超过 20MB',
// 视频上传
uploadVideoTitle: '您在设计过程中如何使用 AiDA',
clickToUploadVideo: '点击选择或拖拽文件上传',
videoFileLimit: '视频文件MP4, MOV1080P不超过100MB',
// 条款与条件
termsAndConditions: '参赛条款',
conditionFirst: '我确认所提交的作品均为原创,且由我本人独立创作。',
conditionSecond:
'我知悉 Code-Create 对提交的所有设计及视频享有市场宣传和推广权利。',
conditionThird: '我同意在入围决赛后参加相关活动,包括 AiDA 培训及颁奖典礼。',
conditionFourth: '我希望接收有关 AiDA 产品及未来比赛的最新信息。(可选)',
// 提交按钮
submitYourDesign: '提交作品',
unfinishedFormTip: 'AiDA 平台内消息中的链接可保存您未完成的表单。',
// 验证码弹窗
checkYourEmail: '请查看您的邮箱',
enterSixDigitCode: '请输入发送到邮箱的 6 位验证码',
verify: '验证',
resendCode: '重新发送',
resendCodeIn: '重新发送',
// 验证消息
verificationSuccess: '验证成功!',
pleaseVerifyEmailFirst: '请先验证您的邮箱',
pleaseCheckTerms: '请同意参赛条款',
pleaseFillRequiredFields: '请填写所有必填项',
pleaseEnterCompleteCode: '请输入完整的6位验证码',
// 上传状态
fileUploadedSuccess: '文件上传成功。',
fileUploadFailed: '文件上传失败。',
// 验证器消息
pleaseInputEmail: '请输入邮箱地址',
pleaseInputValidEmail: '请输入有效的邮箱地址',
pleaseInputFirstName: '请输入您的名',
pleaseInputLastName: '请输入您的姓',
pleaseSelectGender: '请选择您的性别',
pleaseInputOccupation: '请输入您的职业',
pleaseInputAge: '请输入您的年龄',
pleaseInputCountry: '请输入您的国家/地区及城市',
pleaseInputPhoneNumber: '请输入您的电话号码',
pleaseInputValidPhone: '请输入有效的电话号码',
pleaseInputDigits: '请输入数字',
pleaseInputDesignTitle: '请输入您的作品标题',
pleaseInputDesignDescription: '请输入您的设计说明',
pleaseUploadPdf: '请上传您的PDF文件',
pleaseUploadVideo: '请上传您的视频文件',
uploadPdfOnly: '请仅上传 PDF 文件。',
uploadVideoOnly: '请仅上传 MP4 或 MOV 文件。',
fileSizeExceeds: '文件大小超过 {sizeLimit} 限制。请上传较小的文件。',
videoDurationExceeds: '视频时长不可超过60秒',
uploadFailed: '上传失败'
} }
} }

View File

@@ -133,7 +133,8 @@ export default {
UploadOpenimage: 'Upload/Open image', UploadOpenimage: 'Upload/Open image',
jsContent1: jsContent1:
"Have you saved your canvas content? If not, please click 'Save' before closing.", "Have you saved your canvas content? If not, please click 'Save' before closing.",
jsContent2: 'We only provide super-resolution capabilities for printboard images.', jsContent2:
'We only provide super-resolution capabilities for printboard images.',
jsContent3: 'Your points are less than one SR', jsContent3: 'Your points are less than one SR',
jsContent4: 'Your points balance is insufficient', jsContent4: 'Your points balance is insufficient',
jsContent5: jsContent5:
@@ -555,7 +556,8 @@ export default {
inputContent1: 'Input prompt', inputContent1: 'Input prompt',
GeneratePrint: 'Pattern', GeneratePrint: 'Pattern',
maximumLength: 'The entered content exceeds the maximum length.', maximumLength: 'The entered content exceeds the maximum length.',
PatternTitle: 'Generates repeatable designs that can be fully tiled across garments.', PatternTitle:
'Generates repeatable designs that can be fully tiled across garments.',
LogoTitle: LogoTitle:
'Creates standalone graphic designs that can be placed individually or tiled.', 'Creates standalone graphic designs that can be placed individually or tiled.',
SloganTitle: SloganTitle:
@@ -1004,21 +1006,15 @@ export default {
subscriptionRenewal: 'There are no subscription plans with automatic renewal.' subscriptionRenewal: 'There are no subscription plans with automatic renewal.'
}, },
guide: { guide: {
guide1: guide1: "You can personalize your design settings right here in the <strong>Workspace</strong>, including choosing to design for men's or women's wear, as well as selecting the mannequin to use for your creations.",
"You can personalize your design settings right here in the <strong>Workspace</strong>, including choosing to design for men's or women's wear, as well as selecting the mannequin to use for your creations.",
guide2: "Select the apparel type you'd like to work on.", guide2: "Select the apparel type you'd like to work on.",
guide3: 'Change the mannequin here.', guide3: 'Change the mannequin here.',
guide4: guide4: 'You can currently select a mannequin from our system library. Later, you can also choose from the user library after registering your own mannequin.',
'You can currently select a mannequin from our system library. Later, you can also choose from the user library after registering your own mannequin.',
guide5: 'Begin your creative journey here. ', guide5: 'Begin your creative journey here. ',
guide6: guide6: 'For the Moodboard, Printboard, or Sketchboard, we provide three different sourcing methods to add images. The first option is <strong>Upload</strong>, allowing you to <stront>upload</stront> directly from your local device.',
'For the Moodboard, Printboard, or Sketchboard, we provide three different sourcing methods to add images. The first option is <strong>Upload</strong>, allowing you to <stront>upload</stront> directly from your local device.', guide7: "The second method is to select from your <strong>Library</strong>. <br> You might notice that your library page is currently empty; there's no need to worry. All the images you upload will be automatically added to your library. In the future, you won't have to upload each time—you can simply choose from your library instead.",
guide7: guide8: 'The third method is to <strong>Generate</strong> images using the latest Image Generation technology.',
"The second method is to select from your <strong>Library</strong>. <br> You might notice that your library page is currently empty; there's no need to worry. All the images you upload will be automatically added to your library. In the future, you won't have to upload each time—you can simply choose from your library instead.", guide9: 'Enter keywords that capture the mood you wish to express and then click the <strong>Low Quality</strong> button.',
guide8:
'The third method is to <strong>Generate</strong> images using the latest Image Generation technology.',
guide9:
'Enter keywords that capture the mood you wish to express and then click the <strong>Low Quality</strong> button.',
guide10: 'Select two images for your moodboard.', guide10: 'Select two images for your moodboard.',
guide11: 'Click here to layout your moodboard.', guide11: 'Click here to layout your moodboard.',
guide12: 'Click here for next step.', guide12: 'Click here for next step.',
@@ -1068,7 +1064,8 @@ export default {
guide52: 'Click here to generate the product image.', guide52: 'Click here to generate the product image.',
guide53: 'Click this button to apply more tools to the product image. ', guide53: 'Click this button to apply more tools to the product image. ',
guide54: 'We can adjust the lighting and background of this image. ', guide54: 'We can adjust the lighting and background of this image. ',
guide55: 'Click here to generate a product image with lighting from the right side.', guide55:
'Click here to generate a product image with lighting from the right side.',
guide56: 'If you like this result, click the little heart to save it.', guide56: 'If you like this result, click the little heart to save it.',
guide57: 'Click here to go to the export page. ', guide57: 'Click here to go to the export page. ',
guide58: 'You can share your work to the gallery or export to your local device.', guide58: 'You can share your work to the gallery or export to your local device.',
@@ -1184,7 +1181,7 @@ export default {
CanvasTitle: { CanvasTitle: {
ModifySketch: 'Modify Sketch', ModifySketch: 'Modify Sketch',
ModifyItem: 'Modify Item', ModifyItem: 'Modify Item',
RedGreen: 'Edit Front and Back Section', RedGreen: 'Edit Front and Back Section'
}, },
Canvas: { Canvas: {
Canvas: 'Canvas', Canvas: 'Canvas',
@@ -1232,7 +1229,8 @@ export default {
touchDevicePrompts_2: 'Double-click an element to quickly enter edit mode.', touchDevicePrompts_2: 'Double-click an element to quickly enter edit mode.',
touchDevicePrompts_3: 'Two-finger drag to pan the canvas.', touchDevicePrompts_3: 'Two-finger drag to pan the canvas.',
touchDevicePrompts_4: 'Pinch to zoom.', touchDevicePrompts_4: 'Pinch to zoom.',
touchDevicePrompts_5: "Two-finger tap to display the element's transform handles.", touchDevicePrompts_5:
"Two-finger tap to display the element's transform handles.",
touchDevicePrompts_6: 'Three-finger swipe left or right to undo/redo.', touchDevicePrompts_6: 'Three-finger swipe left or right to undo/redo.',
TheDetectedPlatform: 'Guide Detected Platform', TheDetectedPlatform: 'Guide Detected Platform',
BasicOperations: 'Basic Operations', BasicOperations: 'Basic Operations',
@@ -1363,7 +1361,8 @@ export default {
furCurvature: 'Curvature', furCurvature: 'Curvature',
furCurvatureDescription: 'Control the degree of hair curvature', furCurvatureDescription: 'Control the degree of hair curvature',
randomizeDirection: 'Random Cirection', randomizeDirection: 'Random Cirection',
randomizeDirectionDescription: 'Whether to randomize the direction of hair growth', randomizeDirectionDescription:
'Whether to randomize the direction of hair growth',
//水墨 //水墨
InkSettings: 'Ink painting settings', InkSettings: 'Ink painting settings',
InkAmount: 'The amount of ink', InkAmount: 'The amount of ink',
@@ -1389,7 +1388,8 @@ export default {
//马克笔 //马克笔
MarkerSettings: 'Marker Settings', MarkerSettings: 'Marker Settings',
MarkerWidth: 'Brush stroke width', MarkerWidth: 'Brush stroke width',
MarkerWidthDescription: 'Control the width of the brush strokes with the marker pen', MarkerWidthDescription:
'Control the width of the brush strokes with the marker pen',
MarkerCapStyle: 'Writing style', MarkerCapStyle: 'Writing style',
MarkerCapStyleDescription: 'Set the shape of the marker pen tip', MarkerCapStyleDescription: 'Set the shape of the marker pen tip',
MarkerCapStyleRound: 'Round', MarkerCapStyleRound: 'Round',
@@ -1520,15 +1520,18 @@ export default {
CompositeMultiply: 'Multiply', CompositeMultiply: 'Multiply',
CompositeMultiplyTip: 'Multiply: Darken the image', CompositeMultiplyTip: 'Multiply: Darken the image',
CompositeColorBurn: 'Color Burn', CompositeColorBurn: 'Color Burn',
CompositeColorBurnTip: 'Color Burn: Increase contrast and darken the bottom color', CompositeColorBurnTip:
'Color Burn: Increase contrast and darken the bottom color',
CompositeLighten: 'Lighten', CompositeLighten: 'Lighten',
CompositeLightenTip: 'Lighten: Take the brightest color', CompositeLightenTip: 'Lighten: Take the brightest color',
CompositeScreen: 'Screen', CompositeScreen: 'Screen',
CompositeScreenTip: 'Screen: Lighten the image', CompositeScreenTip: 'Screen: Lighten the image',
CompositeColorDodge: 'Color Dodge', CompositeColorDodge: 'Color Dodge',
CompositeColorDodgeTip: 'Color Dodge: Reduce contrast and lighten the bottom color', CompositeColorDodgeTip:
'Color Dodge: Reduce contrast and lighten the bottom color',
CompositeLighter: 'Color Dodge (Add)', CompositeLighter: 'Color Dodge (Add)',
CompositeLighterTip: 'Color Dodge (Add): Add the brightness of the overlapping parts', CompositeLighterTip:
'Color Dodge (Add): Add the brightness of the overlapping parts',
CompositeOverlay: 'Overlay', CompositeOverlay: 'Overlay',
CompositeOverlayTip: 'Overlay: Highlight effect', CompositeOverlayTip: 'Overlay: Highlight effect',
CompositeSoftLight: 'Soft Light', CompositeSoftLight: 'Soft Light',
@@ -1559,7 +1562,7 @@ export default {
PointSelection: 'Point Selection', PointSelection: 'Point Selection',
MarqueeSelection: 'Marquee Selection', MarqueeSelection: 'Marquee Selection',
BrushSelection: 'Brush Selection', BrushSelection: 'Brush Selection',
Erase: 'Erase', Erase: 'Erase'
}, },
speedList: { speedList: {
High: 'High', High: 'High',
@@ -1583,7 +1586,8 @@ export default {
LiquefactionTool: 'Liquefaction Tool' LiquefactionTool: 'Liquefaction Tool'
}, },
event: { event: {
back: 'Back' back: 'Back',
detail:'View Details'
}, },
admin: { admin: {
allUser: 'All User', allUser: 'All User',
@@ -1657,7 +1661,7 @@ export default {
Cancel: 'Cancel', Cancel: 'Cancel',
SelectPlan: 'Select Plan', SelectPlan: 'Select Plan',
AllPlan: 'All', AllPlan: 'All',
PlanStart:'This plan will be actived from', PlanStart: 'This plan will be actived from'
}, },
Login: { Login: {
Login: 'Login', Login: 'Login',
@@ -1665,8 +1669,7 @@ export default {
ForgotPassword: '忘记密码', ForgotPassword: '忘记密码',
Welcome: 'Welcome to', Welcome: 'Welcome to',
AiDA: 'AiDA', AiDA: 'AiDA',
Slogan: Slogan: 'AiDA, a first-to-market technology that empowers fashion designers, based on their creative inspirations, to work with AI to create original designs.',
'AiDA, a first-to-market technology that empowers fashion designers, based on their creative inspirations, to work with AI to create original designs.',
LoginMethod: 'Continue with one of these:', LoginMethod: 'Continue with one of these:',
Individual: 'Individual', Individual: 'Individual',
Academic: 'Academic', Academic: 'Academic',
@@ -1707,7 +1710,8 @@ export default {
IncorrectEmail: 'The email format is incorrect', IncorrectEmail: 'The email format is incorrect',
IncorrectEmailFormat: 'The email format is incorrect', IncorrectEmailFormat: 'The email format is incorrect',
CompleteVerificationCode: 'Please enter the complete verification code.', CompleteVerificationCode: 'Please enter the complete verification code.',
PleaseEnterYourAccountNumberOrPassword: 'Please enter your account number or password' PleaseEnterYourAccountNumberOrPassword:
'Please enter your account number or password'
}, },
LoginSchool: { LoginSchool: {
School: 'School', School: 'School',
@@ -1738,7 +1742,8 @@ export default {
IncorrectEmail: 'The email format is incorrect', IncorrectEmail: 'The email format is incorrect',
IncorrectEmailFormat: 'The email format is incorrect', IncorrectEmailFormat: 'The email format is incorrect',
CompleteVerificationCode: 'Please enter the complete verification code.', CompleteVerificationCode: 'Please enter the complete verification code.',
PleaseEnterYourAccountNumberOrPassword: 'Please enter your account number or password' PleaseEnterYourAccountNumberOrPassword:
'Please enter your account number or password'
}, },
LoginEnterprise: { LoginEnterprise: {
Enterprise: 'Enterprise', Enterprise: 'Enterprise',
@@ -1768,6 +1773,265 @@ export default {
IncorrectEmail: 'The email format is incorrect', IncorrectEmail: 'The email format is incorrect',
IncorrectEmailFormat: 'The email format is incorrect', IncorrectEmailFormat: 'The email format is incorrect',
CompleteVerificationCode: 'Please enter the complete verification code.', CompleteVerificationCode: 'Please enter the complete verification code.',
PleaseEnterYourAccountNumberOrPassword: 'Please enter your account number or password' PleaseEnterYourAccountNumberOrPassword:
'Please enter your account number or password'
},
AwardsPage: {
submitApplication: 'Submit your Application',
applicationDeadline: 'Application Deadline:15th July 2026',
howToApply: 'How to Apply',
stepByStep: 'Step by step',
step1Title: 'Step 1. Become an\nAiDA Subscriber',
step1Desc:
'All applicants must be active\nAiDA subscribers at the time of\nsubmission. You may subscribe\nunder either a monthly or yearly plan.',
step2Title: 'Step 2. Create Your Design Using AiDA',
step2Desc:
'Applicants must create their\ndesigns exclusively using the\nAiDA platform. ',
step2ListTitle: 'Your work should clearly demonstrate:',
step2List: [
'· How AiDA is used as a creative tool',
'· Your design concept and creative direction',
'· The intergration of AI and human creativity'
],
step3Title: 'Step 3. Prepare Your Submission',
processVideo: 'Process Video',
processVideoDesc:
'Include a screenrecorded video\nyour creative process\nusing AiDA.',
videoRequirements: 'Video requirements:',
videoFormat: 'Format: MP4',
videoResolution: 'Resolution: 1080×1920 px',
videoDuration: 'Duration: Maximum 1 minute',
videoSize: 'File size: Maximum 20MB',
fileName: 'File Name',
fileNameDesc: 'AiDAGlobalDesignAward\n2026_[Your Full Name]',
designPortfolio: 'Design Portfolio(PDF)',
submitPdf: 'Submit one single PDF file that includes:',
requiredStructure: 'Required structure:',
pdfDesignTitle: 'Design title',
pdfMoodboard: 'Moodboard',
pdfConcept: 'Concept explanation',
pdfConceptDesc: '(How to use AiDA to develop design)',
pdfRequirements: 'PDF requirements:',
pdfMaxPages: 'Maximum 15 pages',
pdfMaxSize: 'Maximum file size: 20MB',
pdfLanguage: 'Language: English or native language\nwith English translation',
step4Title: 'Step 4. Finalist Requirement',
step4Subtitle: '(for top 20 Designers)',
step4Desc:
'The 20 finalists will be required to\nsubmit physical garments for final\nevaluation',
finalistPieces: 'Number of pieces: 1 full outfit',
finalistBasedOn:
'Garments must be produced\nbased on the submitted\nAiDA-generated designs',
finalistShipping: 'Shipping instructions will be provided by\nCode-create',
bloomYourCreativity: 'Bloom Your Creativity',
themeOf2026: 'Theme of 2026',
bloomText: {
desc1: {
regular1: 'The',
bold1: 'AiDA Global Design Award 2026',
regular2: 'is an ',
bold2: 'international design competition ',
regular3: 'hosted by ',
bold3: 'Code-create ',
regular4: ', a globally leading\n',
bold4: 'AI fashion solutions provider,',
regular5:
'celebrating the future of creativity powered by artificial intelligence.\nBringing together designers from around the world, AiDA empowers AI as a creative partner—pushing fashion beyond traditional boundaries and unlocking new possibilities where technology amplifies human imagination.'
},
desc2: {
regular1: 'Under the theme',
bold1: '“Where Imagination Meets Innovation, Creativity Blooms,” ',
regular2:
'participants are invited to transform bold ideas into extraordinary designs, seamlessly merging human artistry with artificial intelligence to shape the next era of fashion.'
}
},
bloomDesc1:
'The AiDA Global Design Award 2026 is an\ninternational design competition hosted by\nCodeCreate, a globally leading AI fashion solutions provider,\ncelebrating the future of creativity powered by artificial intelligence.\nBringing together designers from around the world, AiDA empowers AI as a creative partner—pushing fashion beyond traditional boundaries and unlocking new possibilities where technology amplifies human imagination.',
bloomDesc2:
'Under the theme “Where Imagination Meets Innovation, Creativity Blooms,” participants are invited to transform bold ideas into extraordinary designs, seamlessly merging human artistry with artificial intelligence to shape the next era of fashion.',
panelOfJudges: 'Panel of Judges',
expertise: 'Expertise',
judgesHat: {
jae: 'Code-create\nKorea Branch Director\nBesfxxk creative director',
diego: 'Co-founder & Chief Father\nOfficer of OnTheList\n(Hong Kong)',
gregory: 'Senior Designer at\nGabriela Heasrst (Italy)',
vincenzo: 'Cheif Editor of SCMP Style\n(Hong Kong)',
tim: 'Group Fashion Direction of\n Modern Media Group\n(Shanghai)',
desmond: 'Cheif Editor of Vogue\n(Singapore)'
},
awardPrizes: 'Award & Prizes',
recognition: 'Recognition',
grandMoney: 'US$5,000',
goldMoney: 'US$3,000',
silverMoney: 'US$1,000',
grandAwards: 'Grand Awards',
goldAwards: 'Gold Awards',
silverAwards: 'Silver Awards',
finalists: 'Finalists',
cashAward: 'Cash Award',
awardCertificate: 'Award Certificate',
globalMediaExposure: 'Global Media Exposure',
awardCertification: 'Award\nCertification',
TravelAllowance: 'Travel Allowance',
selectionCriteria: 'Selection Criteria',
evaluation: 'Evaluation',
originality: 'Originality',
originalityDesc: 'Unique perspective and\ninnovative approach to\nfashion design',
creativity: 'Creativity',
creativityDesc: 'Artistic vision and exceptional\ndesign excellence',
aidaIntegration: 'AiDA Integration',
aidaIntegrationDesc: 'Effective application of\nAiDA functions',
execution: 'Execution',
executionDesc: 'Quality of presentation and\ntechnical craftsmanship',
totalCashPrizes: 'UP TO\nUS$9000',
totalCashPrizesLabel: 'In total cash prizes',
globalMediaExpose: 'GLOBAL MEDIA\nEXPOSE',
globalMediaExposeLabel: 'Showcased by top\ninternational media platforms',
networkingOpportunities: 'NETWORKING\nOPPORTUNITIES',
networkingOpportunitiesLabel:
'Build connections with\ndesigners and industry leaders',
awardCeremonyHongKong: 'AWARD CEREMONY\nIN HONG KONG',
awardCeremonyLabel: 'Travel allowance\nprovided for finalists',
competitionTimeline: 'Competition Timeline',
shapingTheFuture: 'Shaping the Future',
timelineApplicationLabel: 'Application',
timelineDeadlineLabel: 'Deadline',
timeJul15: 'Jul 15',
applicationDeadlineDesc:
'Application deadline and\nentry review process\nbegins.',
twentyFinalistsAnnounced: '20 Finallists',
announcedLabel: 'Announced',
timeAug30: 'Aug 30',
twentyFinalistsDesc:
'Announcement of 20\nfinalists entering final\nevaluation stage.',
finalistSubmission: 'Finallist\nSubmission',
submissionLabel: 'Deadline',
timeSept30: 'Sept 30',
finalistSubmissionDesc:
'Finalists submit\ncompleted outfits for\nfinal assessment.',
receivingOutfits: 'Receiving Outfits',
fromFinalistsLabel: 'from Finallists',
timeOctober: 'October',
receivingOutfitsDesc: 'AiDA receives physical\noutfits from all 20\nfinalists.',
awardCeremony: 'Award',
ceremonyLabel: 'Ceremony',
timeNov12: 'Nov 12',
awardCeremonyDesc: 'Award Ceremony &\nCommunity Gathering\n Soho House.',
submissionSuccessful: 'Submission Successful',
submissionSuccessfulDesc:
'Please review your submitted information in the AiDA in-platform message.\nYou may edit it if needed. Competition updates and results will be sent via email.',
deadlinePassed: 'Application Deadline Passed',
deadlinePassedDesc:
'The submission deadline for AiDA Global Fashion Award 2026 has ended.\nWe are no longer accepting new applications.',
uploadInProgress: 'Upload in progress…',
uploadSuccess: 'Uploaded Successfully',
uploadFailed: 'Upload failed',
pdfFileTip: 'PDF file, max 20MB',
videoFileTip: 'Video file (MP4, MOV), 1080p, max 100MB',
wechatTitle: 'WeChat Official Account',
wechatDesc: 'Scan the QR code in WeChat'
},
AwardApply: {
// 页面主标题区域
applicationForm: 'Application Form',
emailVerification: 'Email Verification',
aidaUsersOnly: 'AiDA Users Only',
slogan: 'BLOOM YOUR CREATIVITY • AIDA GLOBAL DESIGN AWARDS 2026',
// 邮箱验证部分
emailAddress: 'Email Address',
sendCode: 'Send Code',
pleaseUseRegisteredEmail:
'Please use the email address you registered with AiDA.',
// 个人信息部分
personalInformation: 'Personal Information',
tellUsAboutYourself: 'Tell us about yourself',
firstName: 'First Name',
lastName: 'Last Name',
gender: 'Gender',
occupation: 'Occupation',
age: 'Age',
countryRegionCity: 'Country/Region and City',
phoneNumber: 'Phone Number',
portfolioUrl: 'Portfolio Website/Instagram (Optional)',
// 性别选项
male: 'Male',
female: 'Female',
other: 'Other',
// 设计信息部分
designInformation: 'Design Information',
shareYourCreativeVision: 'Share your creative vision',
designTitle: 'Design Title',
designDescription: 'Design description',
designDescriptionPlaceholder:
'Briefly describe your design concept, inspiration, and creative direction...',
// 提交文件部分
submissionFiles: 'Submission Files',
uploadYourDesignMaterials: 'Upload your design materials',
submissionRequirements: 'Submission Requirements',
pdfRequirement: `Single PDF file\n Title, mood board, elaboration\n+ 4 outfit design with materials (max 15 pages)`,
rightContent: {
format: 'Format: Single PDF file, 15 pages, maximum 20MB',
video: `Video: Design process, 1080×1920 pixels (9:16 ratio), maximum 60 seconds`
},
// PDF 上传
uploadPdfTitle: 'How will you use AiDA in your design process?',
clickToUploadPdf: 'Click to upload or drag and drop',
pdfFileLimit: 'PDF file, max 20MB',
// 视频上传
uploadVideoTitle: 'How will you use AiDA in your design process?',
clickToUploadVideo: 'Click to upload or drag and drop',
videoFileLimit: 'Video file (MP4, MOV), 1080p, max 100MB',
// 条款与条件
termsAndConditions: 'Terms & Conditions',
conditionFirst:
'I confirm that all submitted work is original and created by me.',
conditionSecond:
'I understand that Code-Create has marketing and promotional rights to all submitted designs and videos.',
conditionThird:
'I agree to participate in finalist activities if selected, including AiDA training and award ceremony.',
conditionFourth:
'I would like to receive updates about AiDA products and future competitions. (Optional)',
// 提交按钮
submitYourDesign: 'Submit your Design',
unfinishedFormTip:
'The link in the AiDA in-platform message will save your unfinished form.',
// 验证码弹窗
checkYourEmail: 'Check your email',
enterSixDigitCode: 'Enter the 6-digit code sent to',
verify: 'Verify',
resendCode: 'Resend',
resendCodeIn: 'Resend Code in',
// 验证消息
verificationSuccess: 'Verification successful!',
pleaseVerifyEmailFirst: 'Please verify your email first',
pleaseCheckTerms: 'Please agree to the terms and conditions',
pleaseFillRequiredFields: 'Please fill in all the required fields',
pleaseEnterCompleteCode: 'Please enter the complete 6-digit verification code',
// 上传状态
fileUploadedSuccess: '{fileName} file uploaded successfully.',
fileUploadFailed: '{fileName} file upload failed.',
// 验证器消息
pleaseInputEmail: 'Please input the email address',
pleaseInputValidEmail: 'Please input a valid email address',
pleaseInputFirstName: 'Please input your first name',
pleaseInputLastName: 'Please input your last name',
pleaseSelectGender: 'Please select your gender',
pleaseInputOccupation: 'Please input your occupation',
pleaseInputAge: 'Please input your age',
pleaseInputCountry: 'Please input your country/region and city',
pleaseInputPhoneNumber: 'Please enter your phone number.',
pleaseInputValidPhone: 'Please enter a valid phone number.',
pleaseInputDigits: 'Please enter digits only',
pleaseInputDesignTitle: 'Please input your design title',
pleaseInputDesignDescription: 'Please input your design description',
pleaseUploadPdf: 'Please upload your PDF',
pleaseUploadVideo: 'Please upload your video',
uploadPdfOnly: 'Please upload a PDF file only.',
uploadVideoOnly: 'Please upload a MP4 or MOV file only.',
fileSizeExceeds:
'File size exceeds {sizeLimit} limit. Please upload a smaller file.',
videoDurationExceeds:
'Video duration exceeds 60 seconds limit. Please upload a shorter video.',
uploadFailed: 'Upload failed'
} }
} }

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:{

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +1,152 @@
<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
class="title animation-element"
ref="applyTitleRef"
>
{{ $t('AwardsPage.howToApply') }}
</div>
<div
class="sub-title animation-element"
ref="applySubTitleRef"
>
{{ $t('AwardsPage.stepByStep') }}
</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">{{ $t(item.type) }}</div>
</div>
<div class="context-container flex flex-center">
<div
class="context"
v-for="el in item.desc"
>
{{ $t(el) }}
</div>
<div
class="list"
v-if="item.listTitle"
>
<div class="list-title">{{ $t(item.listTitle) }}</div>
<ul class="list-items">
<li
class="list-item"
v-for="el in item.list"
>
{{ $t(el) }}
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="bottom flex">
<div class="step-3 flex flex-col animation-element" ref="step3Ref">
<div class="header">{{ $t('AwardsPage.step3Title') }}</div>
<div class="content flex">
<div class="content-left flex flex-col space-between">
<div class="content-item">
<div class="item-header flex align-center"> <div class="item-header flex align-center">
<img src="@/assets/images/award/bloom_logo.png" class="logo" /> <div class="point"></div>
<div class="item-title">{{ item.type }}</div> <div>{{ $t('AwardsPage.processVideo') }}</div>
</div> </div>
<div class="context" v-for="el in item.desc"> <div class="desc-wrapper flex flex-col space-between">
{{ el }} <div class="item-desc">
{{ $t('AwardsPage.processVideoDesc') }}
</div>
<ul class="desc-lists">
<div class="desc-lists-title">
{{ $t('AwardsPage.videoRequirements') }}
</div>
<li>{{ $t('AwardsPage.videoFormat') }}</li>
<li>{{ $t('AwardsPage.videoResolution') }}</li>
<li>{{ $t('AwardsPage.videoDuration') }}</li>
<li>{{ $t('AwardsPage.videoSize') }}</li>
</ul>
</div> </div>
</div> </div>
</div> <div class="content-item">
<div class="right">
<div class="item-box">
<div class="item-box">
<div class="item-header flex align-center"> <div class="item-header flex align-center">
<img src="@/assets/images/award/bloom_logo.png" class="logo" /> <div class="point"></div>
<div class="item-title">{{ rightRequirment.type }}</div> <div>{{ $t('AwardsPage.fileName') }}</div>
</div>
<div class="item-desc indent">
{{ $t('AwardsPage.fileNameDesc') }}
</div>
</div>
</div>
<div class="content-right">
<div class="content-item flex flex-col">
<div class="item-header flex align-center">
<div class="point"></div>
<div>{{ $t('AwardsPage.designPortfolio') }}</div>
</div>
<div
class="desc-wrapper flex-1 flex flex-col space-between"
>
<ul class="desc-lists">
<div class="desc-lists-title">
<p>
{{ $t('AwardsPage.submitPdf') }}
</p>
<p>{{ $t('AwardsPage.requiredStructure') }}</p>
</div>
<li>{{ $t('AwardsPage.pdfDesignTitle') }}</li>
<li>{{ $t('AwardsPage.pdfMoodboard') }}</li>
<li>{{ $t('AwardsPage.pdfConcept') }}</li>
<div>{{ $t('AwardsPage.pdfConceptDesc') }}</div>
</ul>
<ul class="desc-lists">
<div class="desc-lists-title">
<p>{{ $t('AwardsPage.pdfRequirements') }}</p>
</div>
<li>{{ $t('AwardsPage.pdfMaxPages') }}</li>
<li>{{ $t('AwardsPage.pdfMaxSize') }}</li>
<li>
{{ $t('AwardsPage.pdfLanguage') }}
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="step-4 animation-element" ref="step4Ref">
<div class="header flex flex-col flex-center">
<p>{{ $t('AwardsPage.step4Title') }}</p>
<p class="sub-title">{{ $t('AwardsPage.step4Subtitle') }}</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">
{{ $t('AwardsPage.step4Desc') }}
</div>
<li>{{ $t('AwardsPage.finalistPieces') }}</li>
<li>
{{ $t('AwardsPage.finalistBasedOn') }}
</li>
<li>
{{ $t('AwardsPage.finalistShipping') }}
</li>
</ul>
</div> </div>
<div class="context" v-for="el in rightRequirment.desc">
{{ el }}
</div> </div>
</div> </div>
</div> </div>
@@ -32,41 +156,45 @@
</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 { useI18n } from 'vue-i18n'
import { gsap } from 'gsap'
const leftRequirment = ref([ const { t } = useI18n()
const leftRequirment = ref([
{ {
type: 'Video', type: 'AwardsPage.step1Title',
desc: ['The process of doing design'] desc: ['AwardsPage.step1Desc']
}, },
{ {
type: 'Design', type: 'AwardsPage.step2Title',
desc: [ desc: ['AwardsPage.step2Desc'],
'Structure: design title, moodboard and elaboration (how will you use AiDA to design)', listTitle: 'AwardsPage.step2ListTitle',
'Design sketch: Maximum 4 outfit design with proposed materials' list: [
] 'AwardsPage.step2List[0]',
'AwardsPage.step2List[1]',
'AwardsPage.step2List[2]'
],
background: '#F9F9F9'
} }
]) ])
const rightRequirment = ref({ const applyRef = ref()
type: 'Submission Format', const applyTitleRef = ref()
desc: [ const applySubTitleRef = ref()
'Naming as “AiDA global award 2026_applicantname”', const reqListRef = ref()
'Mp4\n(1080x1920pixels/20mb within 1min)', const itemRefs = ref<HTMLElement[]>([])
'Single PDF file\n(within 15 pages, maximum 20mb)', const step3Ref = ref()
'English or native language\nwith English translation' const step4Ref = ref()
]
})
const applyRef = ref<HTMLElement | null>(null) const hasPlayedAnim = ref(false)
const applyTitleRef = ref<HTMLElement | null>(null) let timeline: gsap.core.Timeline | null = null
const applySubTitleRef = ref<HTMLElement | null>(null)
const reqListRef = ref<HTMLElement | null>(null)
const hasPlayedApplyAnim = ref(false)
let applyObserver: IntersectionObserver | null = null
const setupApplyInitialState = () => { let observer: IntersectionObserver | null = null
const setupApplyInitialState = () => {
// 设置标题和副标题的初始状态
const titleEls = [applyTitleRef.value, applySubTitleRef.value].filter( const titleEls = [applyTitleRef.value, applySubTitleRef.value].filter(
Boolean Boolean
) as HTMLElement[] ) as HTMLElement[]
@@ -77,87 +205,121 @@ const setupApplyInitialState = () => {
transformOrigin: '50% 50%' 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 (hasPlayedApplyAnim.value) return const allStepElements: HTMLElement[] = []
const titleEls = [applyTitleRef.value, applySubTitleRef.value].filter( if (itemRefs.value && itemRefs.value.length > 0) {
Boolean allStepElements.push(...itemRefs.value)
) as HTMLElement[] }
const headers = reqListRef.value?.querySelectorAll<HTMLElement>('.item-header') if (step3Ref.value) {
const contexts = reqListRef.value?.querySelectorAll<HTMLElement>('.context') allStepElements.push(step3Ref.value as HTMLElement)
if (!titleEls.length) return }
if (step4Ref.value) {
allStepElements.push(step4Ref.value as HTMLElement)
}
const tl = gsap.timeline({ defaults: { ease: 'power2.out' } }) if (allStepElements.length > 0) {
tl.to(titleEls, { gsap.set(allStepElements, {
opacity: 1, opacity: 0,
y: 50
})
}
}
const initAnimations = () => {
if (hasPlayedAnim.value) return
timeline = gsap.timeline({
defaults: { ease: 'back.out(1.7)' }
})
if (applyTitleRef.value && applySubTitleRef.value) {
timeline.to([applyTitleRef.value, applySubTitleRef.value], {
scale: 1, scale: 1,
opacity: 1,
duration: 0.6, duration: 0.6,
ease: 'back.out(1.6)',
stagger: 0.1 stagger: 0.1
}) })
if (headers?.length) {
tl.to(
headers,
{
opacity: 1,
duration: 0.4,
stagger: 0.1
},
'-=0.1'
)
}
if (contexts?.length) {
tl.to(
contexts,
{
opacity: 1,
duration: 0.4,
stagger: 0.05
},
'-=0.05'
)
} }
hasPlayedApplyAnim.value = true const allStepElements: HTMLElement[] = []
applyObserver?.disconnect() 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)
}
onMounted(() => { if (allStepElements.length > 0) {
timeline.to(allStepElements, {
opacity: 1,
y: 0,
duration: 0.6,
stagger: 0.2
}, '>')
}
hasPlayedAnim.value = true
}
onMounted(() => {
nextTick(() => { nextTick(() => {
setupApplyInitialState() setupApplyInitialState()
if ('IntersectionObserver' in window) { observer = new IntersectionObserver(
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(() => { onBeforeUnmount(() => {
applyObserver?.disconnect() // Cleanup animation timeline
}) if (timeline) {
timeline.kill()
}
// Cleanup IntersectionObserver
if (observer) {
observer.disconnect()
}
})
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.apply-container { p {
margin: 0;
padding: 0;
}
ul {
margin: 0;
padding: 0;
}
.animation-element{
will-change: opacity transform;
}
.apply-container {
flex: 1; flex: 1;
height: 143.3rem;
background: url('@/assets/images/award/apply_bg.png') no-repeat; background: url('@/assets/images/award/apply_bg.png') no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
padding: 12.7rem 0 16.9rem; padding: 12.7rem 21.4rem 12rem;
.title { .title {
text-align: center; text-align: center;
color: #232323; color: #232323;
@@ -172,38 +334,196 @@ onBeforeUnmount(() => {
font-size: 3rem; font-size: 3rem;
font-family: 'Arial'; font-family: 'Arial';
font-weight: 400; font-weight: 400;
margin-bottom: 8.2rem;
} }
.requirments-list { .requirments-list {
flex: 1; flex: 1;
padding-left: 41.4rem; row-gap: 8.2rem;
column-gap: 17.7rem; .top {
margin-top: 12rem; height: 27.4rem;
.left { color: #585858;
color: #232323; column-gap: 4.6rem;
height: 100%; .item-box {
height: 27.4rem;
}
} }
.item-box { .item-box {
border-radius: 0.8rem;
&:nth-of-type(1) {
width: 47rem;
flex-grow: initial;
}
&:nth-of-type(2) {
flex: 1;
}
.item-header { .item-header {
column-gap: 3.2rem; background-color: #424242;
border-radius: 0.8rem;
height: 7.8rem;
.item-title { .item-title {
color: #232323; color: #fff;
font-family: 'PoppinsBold'; font-family: 'PoppinsBold';
font-weight: 600; font-weight: 600;
font-size: 2.8rem; 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 { .context {
margin-top: 4rem; // margin-top: 4rem;
width: 46.8rem; // width: 46.8rem;
text-align: center;
color: #585858; color: #585858;
font-family: 'Arial'; font-family: 'Arial';
font-weight: 400; font-weight: 400;
line-height: 3rem; line-height: 3rem;
font-size: 2.4rem; font-size: 2.4rem;
padding-left: 5.6rem; // padding-left: 5.6rem;
white-space: pre-line; 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,34 +1,59 @@
<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"
> >
Bloom Your Creativity {{ $t('AwardsPage.bloomYourCreativity') }}
</div> </div>
<div <div
class="season" class="season"
ref="subtitleRef" ref="subtitleRef"
> >
Theme of 2026 {{ $t('AwardsPage.themeOf2026') }}
</div> </div>
<div <div
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 {{ $t('AwardsPage.bloomText.desc1.regular1') }}
traditional boundaries. Let your ideas blossom into extraordinary designs that <span class="arial-bold">
merge human artistry with artificial intelligence. {{ $t('AwardsPage.bloomText.desc1.bold1') }}
</span>
{{ $t('AwardsPage.bloomText.desc1.regular2') }}
<span class="arial-bold">
{{ $t('AwardsPage.bloomText.desc1.bold2') }}
</span>
{{ $t('AwardsPage.bloomText.desc1.regular3') }}
<span class="arial-bold">
{{ $t('AwardsPage.bloomText.desc1.bold3') }}
</span>
{{ $t('AwardsPage.bloomText.desc1.regular4') }}
<span class="arial-bold">
{{ $t('AwardsPage.bloomText.desc1.bold4') }}
</span>
{{ $t('AwardsPage.bloomText.desc1.regular5') }}
</p>
<p class="section-2">
{{ $t('AwardsPage.bloomText.desc2.regular1') }}
<span class="arial-bold">
{{ $t('AwardsPage.bloomText.desc2.bold1') }}
</span>
{{ $t('AwardsPage.bloomText.desc2.regular2') }}
</p>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue' import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
import { useI18n } from 'vue-i18n'
import { gsap } from 'gsap' import { gsap } from 'gsap'
const { t } = useI18n()
const titleRef = ref<HTMLElement | null>(null) const titleRef = ref<HTMLElement | null>(null)
const subtitleRef = ref<HTMLElement | null>(null) const subtitleRef = ref<HTMLElement | null>(null)
const textRef = ref<HTMLElement | null>(null) const textRef = ref<HTMLElement | null>(null)
@@ -37,7 +62,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 +85,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 +110,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 +132,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 +154,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 +186,17 @@
} }
.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;
white-space: pre-line;
.section-2 {
margin-top: 4rem;
}
} }
} }
</style> </style>

View File

@@ -1,8 +1,8 @@
<template> <template>
<div class="judges-container flex flex-col align-center"> <div class="judges-container flex flex-col align-center">
<div class="title" ref="judgesTitleRef">Panel of Judges</div> <div class="title" ref="judgesTitleRef">{{ $t('AwardsPage.panelOfJudges') }}</div>
<!-- <img src="@/assets/images/award/bloom_logo.png" class="logo" /> --> <!-- <img src="@/assets/images/award/bloom_logo.png" class="logo" /> -->
<div class="sub-title" ref="judgesSubTitleRef">Expertise</div> <div class="sub-title" ref="judgesSubTitleRef">{{ $t('AwardsPage.expertise') }}</div>
<div class="judgement-list" ref="judgementListRef"> <div class="judgement-list" ref="judgementListRef">
<div <div
class="judgement-item flex flex-col align-center" class="judgement-item flex flex-col align-center"
@@ -10,8 +10,8 @@
:key="item.name" :key="item.name"
> >
<img :src="item.picture" class="picture" /> <img :src="item.picture" class="picture" />
<div class="name">{{ item.name }}</div> <div class="name">{{ $t(item.name) }}</div>
<div class="desc">{{ item.desc }}</div> <div class="desc">{{ $t(item.desc) }}</div>
</div> </div>
</div> </div>
</div> </div>
@@ -19,6 +19,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { onBeforeUnmount, onMounted, nextTick, ref } from 'vue' import { onBeforeUnmount, onMounted, nextTick, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { gsap } from 'gsap' import { gsap } from 'gsap'
import jae from '@/assets/images/award/jae.png' import jae from '@/assets/images/award/jae.png'
import diego from '@/assets/images/award/diego.png' import diego from '@/assets/images/award/diego.png'
@@ -27,36 +28,38 @@ import vincenzo from '@/assets/images/award/vincenzo.png'
import tim from '@/assets/images/award/tim.png' import tim from '@/assets/images/award/tim.png'
import desmond from '@/assets/images/award/desmond.png' import desmond from '@/assets/images/award/desmond.png'
const { t } = useI18n()
const judgements = [ const judgements = [
{ {
picture: jae, picture: jae,
name: 'Jae Hyuk Lim', name: 'Jae Hyuk Lim',
desc: 'Code-create\nKorea Branch Director\nBesfxxk creative director' desc: 'AwardsPage.judgesHat.jae'
}, },
{ {
picture: diego, picture: diego,
name: 'Diego Dultzin Lacoste', name: 'Diego Dultzin Lacoste',
desc: 'Co-founder & Chief Father\nOfficer of OnTheList\n(Hong Kong)' desc: 'AwardsPage.judgesHat.diego'
}, },
{ {
picture: gregory, picture: gregory,
name: 'Gregory de la Hogue Moran', name: 'Gregory de la Hogue Moran',
desc: 'Senior Designer at\nGabriela Heasrst (Italy)' desc: 'AwardsPage.judgesHat.gregory'
}, },
{ {
picture: vincenzo, picture: vincenzo,
name: 'Vincenzo La Torre', name: 'Vincenzo La Torre',
desc: 'Cheif Editor of SCMP Style\n(Hong Kong)' desc: 'AwardsPage.judgesHat.vincenzo'
}, },
{ {
picture: tim, picture: tim,
name: 'Tim Lim', name: 'Tim Lim',
desc: 'Group Fashion Direction of\n Modern Media Group\n(Shanghai)' desc: 'AwardsPage.judgesHat.tim'
}, },
{ {
picture: desmond, picture: desmond,
name: 'Desmond Lim', name: 'Desmond Lim',
desc: 'Cheif Editor of Vogue\n(Singapore)' desc: 'AwardsPage.judgesHat.desmond'
} }
] ]
@@ -199,6 +202,9 @@ onBeforeUnmount(() => {
column-gap: 23.22rem; column-gap: 23.22rem;
row-gap: 8rem; row-gap: 8rem;
padding: 0 25rem 0 26.6rem; padding: 0 25rem 0 26.6rem;
div{
text-align: center;
}
.judgement-item { .judgement-item {
overflow: hidden; overflow: hidden;
.picture { .picture {

View File

@@ -8,14 +8,14 @@
class="title" class="title"
ref="prizesTitleRef" ref="prizesTitleRef"
> >
Award & Prizes {{ $t('AwardsPage.awardPrizes') }}
</div> </div>
<!-- <img src="@/assets/images/award/bloom_logo.png" class="logo" /> --> <!-- <img src="@/assets/images/award/bloom_logo.png" class="logo" /> -->
<div <div
class="desc" class="desc"
ref="prizesSubTitleRef" ref="prizesSubTitleRef"
> >
Recongnition {{ $t('AwardsPage.recognition') }}
</div> </div>
</div> </div>
<div <div
@@ -24,17 +24,20 @@
> >
<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">
<div class="prize-name">{{ item.name }}</div> {{ $t(item.money) }}
</div>
<div class="prize-name">{{ $t(item.name) }}</div>
<div class="prize-desc flex flex-col flex-center"> <div class="prize-desc flex flex-col flex-center">
<div <div
class="desc-item" class="desc-item"
v-for="el in item.desc" v-for="el in item.desc"
> >
{{ el }} {{ $t(el) }}
</div> </div>
</div> </div>
</div> </div>
@@ -44,28 +47,51 @@
<script setup lang="ts"> <script setup lang="ts">
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue' import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { gsap } from 'gsap' import { gsap } from 'gsap'
const { t } = useI18n()
const props = defineProps({
isZh: {
type: Boolean,
default: false
}
})
const prizes = [ const prizes = [
{ {
money: 'US$5000', money: 'AwardsPage.grandMoney',
name: 'Grand Prize', name: 'AwardsPage.grandAwards',
desc: ['Cash Award', 'Award Ceritificate', 'Global Media Exposure'] desc: [
'AwardsPage.cashAward',
'AwardsPage.awardCertificate',
'AwardsPage.globalMediaExposure'
]
}, },
{ {
money: 'US$3000', money: 'AwardsPage.goldMoney',
name: 'First Runner-Up', name: 'AwardsPage.goldAwards',
desc: ['Cash Award', 'Award Ceritificate', 'Global Media Exposure'] desc: [
'AwardsPage.cashAward',
'AwardsPage.awardCertificate',
'AwardsPage.globalMediaExposure'
]
}, },
{ {
money: 'US$2000', money: 'AwardsPage.silverMoney',
name: 'Second Runner-Up', name: 'AwardsPage.silverAwards',
desc: ['Cash Award', 'Award Ceritificate', 'Global Media Exposure'] desc: [
'AwardsPage.cashAward',
'AwardsPage.awardCertificate',
'AwardsPage.globalMediaExposure'
]
}, },
{ {
money: 'Certification', money: 'AwardsPage.awardCertification',
name: 'Finalists', name: 'AwardsPage.finalists',
desc: ['Award Ceritificate', 'Global Media Exposure'] desc: ['AwardsPage.TravelAllowance', 'AwardsPage.globalMediaExposure'],
smaller: !props.isZh
} }
] ]
@@ -214,10 +240,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

@@ -3,9 +3,9 @@
class="selection-container container flex flex-col align-center" class="selection-container container flex flex-col align-center"
ref="selectionRef" ref="selectionRef"
> >
<div class="title">Selection Criteria</div> <div class="title">{{ $t('AwardsPage.selectionCriteria') }}</div>
<!-- <img src="@/assets/images/award/bloom_logo.png" class="logo" /> --> <!-- <img src="@/assets/images/award/bloom_logo.png" class="logo" /> -->
<div class="sub-title">Evaluation</div> <div class="sub-title">{{ $t('AwardsPage.evaluation') }}</div>
<div class="criteria-list flex" ref="criteriaListRef"> <div class="criteria-list flex" ref="criteriaListRef">
<div <div
class="item flex flex-col align-center" class="item flex flex-col align-center"
@@ -13,8 +13,8 @@
:key="item.name" :key="item.name"
> >
<img :src="item.icon" class="icon" :style="item.style" /> <img :src="item.icon" class="icon" :style="item.style" />
<div class="name">{{ item.name }}</div> <div class="name">{{ $t(item.name) }}</div>
<div class="desc">{{ item.desc }}</div> <div class="desc">{{ $t(item.desc) }}</div>
</div> </div>
</div> </div>
</div> </div>
@@ -22,35 +22,38 @@
<script setup lang="ts"> <script setup lang="ts">
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue' import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { gsap } from 'gsap' import { gsap } from 'gsap'
import criteria1 from '@/assets/images/award/criteria_1.png' import criteria1 from '@/assets/images/award/criteria_1.png'
import criteria2 from '@/assets/images/award/criteria_2.png' import criteria2 from '@/assets/images/award/criteria_2.png'
import criteria3 from '@/assets/images/award/criteria_3.png' import criteria3 from '@/assets/images/award/criteria_3.png'
import criteria4 from '@/assets/images/award/criteria_4.png' import criteria4 from '@/assets/images/award/criteria_4.png'
const { t } = useI18n()
const criteriaList = ref([ const criteriaList = ref([
{ {
icon: criteria1, icon: criteria1,
name: 'Originality', name: 'AwardsPage.originality',
desc: 'Unique perspective and innovative approach to fashion design', desc: 'AwardsPage.originalityDesc',
style: { width: '13rem', height: '17rem' } style: { width: '13rem', height: '17rem' }
}, },
{ {
icon: criteria2, icon: criteria2,
name: 'Creativity', name: 'AwardsPage.creativity',
desc: 'Artistic vision and exceptional design excellence', desc: 'AwardsPage.creativityDesc',
style: { width: '16rem', height: '18rem' } style: { width: '16rem', height: '18rem' }
}, },
{ {
icon: criteria3, icon: criteria3,
name: 'AiDA Integration', name: 'AwardsPage.aidaIntegration',
desc: 'Effective application of AI design tools and functions', desc: 'AwardsPage.aidaIntegrationDesc',
style: { width: '16rem', height: '18rem' } style: { width: '16rem', height: '18rem' }
}, },
{ {
icon: criteria4, icon: criteria4,
name: 'Execution', name: 'AwardsPage.execution',
desc: 'Quality of presentation and technical craftsmanship', desc: 'AwardsPage.executionDesc',
style: { width: '18.8rem', height: '18rem' } style: { width: '18.8rem', height: '18rem' }
} }
]) ])
@@ -166,6 +169,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,37 +1,44 @@
<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"
:key="item.number" :key="item.number"
:style="{ '--delay': `${idx * 0.18}s` }" :style="{ '--delay': `${idx * 0.18}s` }"
> >
<div class="number">{{ item.number }}</div> <div class="number">{{ $t(item.number) }}</div>
<div class="label">{{ item.label }}</div> <div class="label">{{ $t(item.label) }}</div>
<div class="line"></div> <div class="line"></div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue' import { computed, ref, onMounted, onUnmounted } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const blocksList = ref([ const blocksList = ref([
{ {
number: 'NETWORKING\n OPPORTUNITIES', number: 'AwardsPage.totalCashPrizes',
label: 'with international\nmedia and designers' label: 'AwardsPage.totalCashPrizesLabel'
}, },
{ {
number: 'INTERNATIONAL\nMEDIA EXPOSE', number: 'AwardsPage.globalMediaExpose',
label: 'through\nleading outlets' label: 'AwardsPage.globalMediaExposeLabel'
}, },
{ {
number: 'UP TO\nUS$9000', number: 'AwardsPage.networkingOpportunities',
label: 'in total prize\npool awards' label: 'AwardsPage.networkingOpportunitiesLabel'
}, },
{ {
number: 'TRAVEL\NALLOWANCE', number: 'AwardsPage.awardCeremonyHongKong',
label: 'for finalists to attend\naward ceremony' label: 'AwardsPage.awardCeremonyLabel'
} }
]) ])
const root = ref<HTMLElement | null>(null) const root = ref<HTMLElement | null>(null)
@@ -40,7 +47,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 +119,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

@@ -5,9 +5,9 @@
alt="" alt=""
class="icon-img" class="icon-img"
/> />
<div class="title">{{ info.title }}</div> <div class="title">{{ $t(info.title) }}</div>
<div class="desc"> <div class="desc">
{{ info.desc }} {{ $t(info.desc) }}
<!-- <div> <!-- <div>
Please review your submitted information in the AiDA in-platform message. Please review your submitted information in the AiDA in-platform message.
</div> </div>
@@ -21,8 +21,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import successIcon from '@/assets/images/award/successful.png' import successIcon from '@/assets/images/award/successful.png'
import expiredIcon from '@/assets/images/award/expired.png' import expiredIcon from '@/assets/images/award/expired.png'
const { t } = useI18n()
const props = defineProps({ const props = defineProps({
isExpired: { isExpired: {
type: Boolean, type: Boolean,
@@ -34,14 +37,14 @@
if (props.isExpired) { if (props.isExpired) {
return { return {
icon: expiredIcon, icon: expiredIcon,
title: 'Application Deadline Passed', title: 'AwardsPage.deadlinePassed',
desc: 'The submission deadline for AIDA Global Fashion Award 2026 has ended.\nWe are no longer accepting new applications. ' desc: 'AwardsPage.deadlinePassedDesc'
} }
} else { } else {
return { return {
icon: successIcon, icon: successIcon,
title: 'Submission Successful', title: 'AwardsPage.submissionSuccessful',
desc: 'Please review your submitted information in the AiDA in-platform message.\nYou may edit it if needed. Competition updates and results will be sent via email.' desc: 'AwardsPage.submissionSuccessfulDesc'
} }
} }
}) })

View File

@@ -3,54 +3,62 @@
ref="containerRef" ref="containerRef"
class="timeline-container container flex flex-col align-center" class="timeline-container container flex flex-col align-center"
> >
<div class="timeline-title">Competition Timeline</div> <div class="timeline-title">{{ $t('AwardsPage.competitionTimeline') }}</div>
<div class="desc">Shaping the Future</div> <div class="desc">{{ $t('AwardsPage.shapingTheFuture') }}</div>
<div class="timeline-point">
<div class="labels-row flex align-center">
<div <div
class="item-label flex flex-col" class="timeline-point"
ref="timelineRef"
>
<!-- 顶部标签行 -->
<div class="grid-row labels-row">
<div
class="grid-cell label-cell"
v-for="item in points" v-for="item in points"
:key="'label-' + item.time" :key="'label-' + item.time"
> >
<div class="main-label">{{ item.label }}</div> <div class="main-label">{{ $t(item.label) }}</div>
<div <div
class="sub-label" class="sub-label"
v-if="item.subLabel" v-if="item.subLabel"
> >
{{ item.subLabel }} {{ $t(item.subLabel) }}
</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>
<div
class="grid-cell icon-cell"
v-for="item in points"
:key="'icon-' + item.time"
>
<img <img
src="@/assets/images/award/point.png" src="@/assets/images/award/point.png"
class="point-icon" class="point-icon"
v-for="item in points"
:key="'icon-' + item.time"
/> />
</div> </div>
</div>
<!-- Times row --> <!-- 时间行 -->
<div class="times-row flex align-center"> <div class="grid-row times-row">
<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 }} {{ $t(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"
> >
<div class="txt"> <div class="txt">
{{ item.desc }} {{ $t(item.desc) }}
</div> </div>
</div> </div>
</div> </div>
@@ -59,34 +67,46 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue' import { nextTick, onBeforeUnmount, onMounted, ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { gsap } from 'gsap' import { gsap } from 'gsap'
const { t } = useI18n()
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: 'AwardsPage.timelineApplicationLabel',
time: 'May', subLabel: 'AwardsPage.timelineDeadlineLabel',
desc: 'Submit your design concept, mood board, and initial sketch.' time: 'AwardsPage.timeJul15',
desc: 'AwardsPage.applicationDeadlineDesc'
}, },
{ {
label: `Top 20`, label: 'AwardsPage.twentyFinalistsAnnounced',
subLabel: 'Collections Finalize', subLabel: 'AwardsPage.announcedLabel',
time: 'June', time: 'AwardsPage.timeAug30',
desc: 'Complete collections, physical garments, and AiDA process videos due.' desc: 'AwardsPage.twentyFinalistsDesc'
}, },
{ {
label: `Top 3`, label: 'AwardsPage.finalistSubmission',
subLabel: 'Finalists Select', subLabel: 'AwardsPage.submissionLabel',
time: 'August', time: 'AwardsPage.timeSept30',
desc: 'Complete collections, physical garments, and AiDA process videos due.' desc: 'AwardsPage.finalistSubmissionDesc'
}, },
{ {
label: 'Award Ceremony', label: 'AwardsPage.receivingOutfits',
time: 'November', subLabel: 'AwardsPage.fromFinalistsLabel',
desc: 'Winners revealed with media coverage and live showcase.' time: 'AwardsPage.timeOctober',
desc: 'AwardsPage.receivingOutfitsDesc'
},
{
label: 'AwardsPage.awardCeremony',
subLabel: 'AwardsPage.ceremonyLabel',
time: 'AwardsPage.timeNov12',
desc: 'AwardsPage.awardCeremonyDesc'
} }
]) ])
@@ -98,17 +118,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 +132,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 +149,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 +193,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 +218,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 +242,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; }
.grid-cell {
display: flex;
justify-content: center; 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 +290,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

@@ -15,13 +15,16 @@
class="progress-icon successful-icon" class="progress-icon successful-icon"
/> />
</div> </div>
<div class="text">{{ text }}</div> <div class="text">{{ $t(text) }}</div>
<div class="tips">{{ tips }}</div> <div class="tips">{{ $t(tips) }}</div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, watch } from 'vue' import { computed, watch } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const props = defineProps<{ const props = defineProps<{
status: string status: string
type: 'pdf' | 'video' type: 'pdf' | 'video'
@@ -29,16 +32,16 @@
const textMap: Record<string, string> = { const textMap: Record<string, string> = {
idle: '', idle: '',
uploading: 'Upload in progress', uploading: 'AwardsPage.uploadInProgress',
success: 'Uploaded Successfully', success:'AwardsPage.uploadSuccess',
error: 'Upload failed' error: 'AwardsPage.fileUploadFailed'
} }
const tips = computed(() => { const tips = computed(() => {
if (props.type === 'pdf') { if (props.type === 'pdf') {
return 'PDF file, max 20MB' return 'AwardsPage.pdfFileTip'
} else if (props.type === 'video') { } else if (props.type === 'video') {
return 'Video file (MP4, MOV), 1080p, max 100MB' return 'AwardsPage.videoFileTip'
} }
return '' return ''
}) })

View File

@@ -1,5 +1,6 @@
<template> <template>
<div class="award-container"> <div class="award-container">
<div class="header-wrapper">
<div class="header flex align-center space-between"> <div class="header flex align-center space-between">
<div class="header-left"> <div class="header-left">
<img <img
@@ -19,6 +20,8 @@
/> />
</div> </div>
</div> </div>
<div class="header-placeholder"></div>
</div>
<router-view /> <router-view />
<div class="footer flex space-between align-center"> <div class="footer flex space-between align-center">
<div class="social-list flex"> <div class="social-list flex">
@@ -80,23 +83,39 @@
class="close-icon" class="close-icon"
@click="handleCloseQRcode" @click="handleCloseQRcode"
/> />
<div class="code-title">WeChat Official Account</div> <div class="code-title">{{ $t('AwardsPage.wechatTitle') }}</div>
<img <img
src="@/assets/images/award/qrcode.jpg" src="@/assets/images/award/qrcode.jpg"
class="qrcode" class="qrcode"
/> />
<div class="tips">Scan the QR code in WeChat</div> <div class="tips">{{ $t('AwardsPage.wechatDesc') }}</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch } from 'vue' import { ref, computed, watch, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { getCookie } from '@/tool/cookie'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const { locale } = useI18n()
onMounted(() => {
// 初始化语言设置
const loginLanguage = localStorage.getItem('loginLanguage')
if (loginLanguage) {
locale.value = loginLanguage
} else {
const userLanguage = getCookie('language')
if (userLanguage) {
locale.value = userLanguage
}
}
})
const showQRcode = ref(false) const showQRcode = ref(false)
const handleCloseQRcode = () => { const handleCloseQRcode = () => {
@@ -107,10 +126,10 @@
const btnType = ref<BtnType>('index') const btnType = ref<BtnType>('index')
const btnText = computed(() => { const btnText = computed(() => {
if (btnType.value === 'index') { if (btnType.value === 'index') {
return 'Submit your Application' return locale.value === 'CHINESE_SIMPLIFIED' ? '提交申请' : 'Submit your Application'
} }
if (btnType.value === 'form') { if (btnType.value === 'form') {
return 'Back to Introduction' return locale.value === 'CHINESE_SIMPLIFIED' ? '赛事介绍' : 'Back to Introduction'
} }
}) })
@@ -142,6 +161,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,12 +170,20 @@
scrollbar-width: none; scrollbar-width: none;
-ms-overflow-style: none; -ms-overflow-style: none;
} }
.header-wrapper {
.header-placeholder {
height: 8rem;
}
.header { .header {
height: 8rem; height: 8rem;
background-color: #232323; background-color: #232323;
padding-left: 21.5rem; padding-left: 21.5rem;
padding-right: 8.6rem; padding-right: 8.6rem;
box-sizing: border-box; box-sizing: border-box;
position: fixed;
top: 0;
width: 100%;
z-index: 9;
.header-left { .header-left {
.logo { .logo {
width: 13rem; width: 13rem;
@@ -175,6 +203,7 @@
} }
} }
} }
}
.footer { .footer {
height: 10rem; height: 10rem;
padding-left: 21.5rem; padding-left: 21.5rem;

View File

@@ -1,17 +1,30 @@
<template> <template>
<div class="award-page"> <div
class="award-page"
:class="{ 'is-zh': isZh }"
>
<div class="banner"> <div class="banner">
<video
:src="bannerUrl"
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"
> >
<div>Submit your Application</div> <div>{{ $t('AwardsPage.submitApplication') }}</div>
<img <img
src="@/assets/images/award/arrow_right.png" src="@/assets/images/award/arrow_right.png"
alt="" alt=""
class="arrow" class="arrow"
/> />
<div class="ddl">Application Deadline:15th March 2026</div> <div class="ddl">{{ $t('AwardsPage.applicationDeadline') }}</div>
</div> </div>
</div> </div>
@@ -19,14 +32,15 @@
<Bloom /> <Bloom />
<TimeLine /> <TimeLine />
<JudgesSection /> <JudgesSection />
<PrizesSection /> <PrizesSection :is-zh="isZh" />
<ApplySection /> <ApplySection />
<SelectionSection /> <SelectionSection />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import JudgesSection from './components/JudgesSection.vue' import JudgesSection from './components/JudgesSection.vue'
import SelectionSection from './components/SelectionSection.vue' import SelectionSection from './components/SelectionSection.vue'
@@ -36,7 +50,19 @@
import Bloom from './components/Bloom.vue' import Bloom from './components/Bloom.vue'
import Slogan from './components/Slogan.vue' import Slogan from './components/Slogan.vue'
import banner from '@/assets/images/award/banner.mp4'
import bannerZh from '@/assets/images/award/banner_chinese.mp4'
const router = useRouter() const router = useRouter()
const { locale } = useI18n()
const isZh = computed(() => {
return locale.value === 'CHINESE_SIMPLIFIED'
})
const bannerUrl = computed(() => {
return isZh.value ? bannerZh : banner
})
const handleSubmitApplication = () => { const handleSubmitApplication = () => {
router.push('/award/contestants') router.push('/award/contestants')
@@ -53,10 +79,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;
@@ -87,11 +118,25 @@
left: 0; left: 0;
text-align: center; text-align: center;
width: 41rem; width: 41rem;
font-family: 'Arial'; font-family: 'ArialBold';
font-weight: 400; font-weight: 700;
font-size: 2rem; font-size: 2rem;
line-height: 2.2rem; line-height: 2.2rem;
color: #232323E5; color: #232323e5;
}
}
}
.is-zh {
.submit-btn {
padding: 0 7.5rem;
height: 7.8rem;
border-radius: 7.74rem;
column-gap: 3.8rem;
// justify-content: space-between;
&,
.ddl {
width: 35.4rem;
} }
} }
} }