Compare commits
297 Commits
7bf1a0bd57
...
research
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae1d7245f4 | ||
| bb021ae9ac | |||
| bfb4e128f5 | |||
| 2f9b33e4ca | |||
|
|
48c37e0810 | ||
| b869a82fae | |||
| e61a8e372d | |||
|
|
f5a74991c9 | ||
|
|
e58e8540c9 | ||
| e75ed7684e | |||
| 918d71072b | |||
|
|
242bc7a01d | ||
|
|
02ad8a340a | ||
|
|
0c250a21b4 | ||
|
|
f781060e7b | ||
|
|
832c9101ab | ||
|
|
c48e836f8e | ||
|
|
6f0780ac2e | ||
|
|
5acb91e584 | ||
|
|
f66ba9e6fa | ||
| 7a90cb8db9 | |||
| dafe87fad8 | |||
| c44747e2c2 | |||
|
|
341c765c73 | ||
|
|
ed6cc294a5 | ||
|
|
a77dc718f9 | ||
|
|
86953a91a1 | ||
|
|
b8f38db351 | ||
| cabbb653bd | |||
| 99533c12b6 | |||
|
|
7fb7ffaced | ||
|
|
59da67e4b4 | ||
|
|
1428f191dd | ||
|
|
13024cdd99 | ||
|
|
fd85ea02c1 | ||
|
|
c196ab6678 | ||
|
|
c005b85c06 | ||
|
|
b50dbbc246 | ||
|
|
01d09f4c34 | ||
|
|
79c9a66296 | ||
|
|
761b1b3512 | ||
|
|
b2cb7378d6 | ||
| 4d9ea75146 | |||
| f7e6926ee9 | |||
| 7aba4e30c9 | |||
| dc1ab330cf | |||
|
|
18c70fe6a3 | ||
|
|
5c746aca4d | ||
|
|
72c4898101 | ||
|
|
a905971dae | ||
| 69643dbc83 | |||
| f3a707d6d8 | |||
| 8f4a43db14 | |||
| 186a158114 | |||
| 3da4a97400 | |||
|
|
96b3636aea | ||
| 228e3d56b5 | |||
| 99ea7eedc7 | |||
| d4fb435db9 | |||
| 0c8b3ee8f1 | |||
| ca782d0aff | |||
|
|
3dfb607b91 | ||
|
|
981b4dad5c | ||
|
|
181e6a87b8 | ||
| 287825b4bf | |||
| 1ffc303721 | |||
|
|
bdf1bb2669 | ||
|
|
1fe79ffcf9 | ||
| 758f63615a | |||
| 20145742c5 | |||
| 8ec9b1bcea | |||
| a9cb6e16e9 | |||
|
|
5690fc6c5b | ||
|
|
9db6a589f0 | ||
|
|
896490e57b | ||
| fca04ba44b | |||
| 25e4fc06c6 | |||
|
|
4bd7740753 | ||
|
|
c428bfd93b | ||
|
|
2a29c6b2cc | ||
|
|
e9d7203804 | ||
|
|
5d7cec520b | ||
|
|
fe72df0c07 | ||
|
|
7c04332290 | ||
|
|
73d912d3cd | ||
|
|
b320294764 | ||
|
|
4913d02c93 | ||
|
|
56916c8d10 | ||
|
|
393a06eceb | ||
|
|
fdb6a87ab4 | ||
| 6d868c7c7a | |||
| 89a89ea5ef | |||
|
|
811e179889 | ||
|
|
0e0eed2566 | ||
|
|
8588c74ffd | ||
|
|
62e7f34c98 | ||
|
|
8f0a56965f | ||
|
|
59422e54d8 | ||
|
|
012f0ef1b5 | ||
|
|
ec4ae4a259 | ||
|
|
8da66d54c0 | ||
|
|
2839953d8e | ||
|
|
2d5d1b7a5e | ||
|
|
33aaf0b600 | ||
|
|
bf4d7bdba8 | ||
|
|
944071201d | ||
|
|
f6556ec9a9 | ||
|
|
8967439d4e | ||
|
|
85ae158952 | ||
|
|
813d2e9645 | ||
|
|
d94ade6641 | ||
|
|
eda893ce10 | ||
|
|
c8cb2de9ab | ||
|
|
7cda2cce27 | ||
| 41893cab86 | |||
| b23531f18b | |||
| de78bfc051 | |||
|
|
0a507fb158 | ||
|
|
a18dead4ff | ||
|
|
76047b763d | ||
| 0930e8cc77 | |||
|
|
9f09a2f31b | ||
|
|
1e0bf83d12 | ||
|
|
1764e2a0bf | ||
|
|
0729917a7e | ||
|
|
b7f7aea0b7 | ||
|
|
4dfa9433fd | ||
|
|
79293901b3 | ||
|
|
03a9e2f52c | ||
|
|
fb1d09d98e | ||
|
|
8ff7a31e92 | ||
|
|
65323febee | ||
|
|
b158341d6e | ||
|
|
44674b5396 | ||
|
|
9cecbdcf9b | ||
|
|
a6b0a60eb6 | ||
|
|
68067aa777 | ||
|
|
ca6fe65dd8 | ||
| 564e179082 | |||
| dc469add22 | |||
| d54e656192 | |||
|
|
ba49b02ebe | ||
|
|
06fa763f26 | ||
|
|
6ad81a1896 | ||
|
|
9b0ec12738 | ||
|
|
26abb2aa88 | ||
|
|
07b7a6f1d7 | ||
| bff3ea8459 | |||
| 29e68757a6 | |||
| 12ea0f7c35 | |||
|
|
920d01a972 | ||
|
|
13b4767992 | ||
|
|
086481bfb9 | ||
|
|
89bdba45be | ||
|
|
8d0b792fd4 | ||
|
|
a6bfca3b2f | ||
|
|
a1b51d5807 | ||
|
|
2f32cee502 | ||
|
|
a05655da1c | ||
|
|
6cdc8c5486 | ||
|
|
972743d3b8 | ||
|
|
df5cb918a2 | ||
|
|
bc8ce0bd47 | ||
|
|
4afe1b637e | ||
|
|
55ede508cb | ||
|
|
fbb66fd192 | ||
|
|
c87b41ae11 | ||
|
|
142c24a947 | ||
|
|
2ee200e1ba | ||
|
|
86db2f22a1 | ||
|
|
9cc012b851 | ||
|
|
c5b7365977 | ||
|
|
43464ae85e | ||
|
|
39c85cd1d1 | ||
|
|
3c77c97532 | ||
|
|
2ab23d0f30 | ||
|
|
85d4569a25 | ||
|
|
cd7d572e43 | ||
| 37157ca94d | |||
| a30897a4b2 | |||
| 9a62d277f2 | |||
|
|
0de5fe276a | ||
|
|
ca17956135 | ||
| 028c6a6540 | |||
| 9d8c3155e6 | |||
|
|
6c921730ef | ||
|
|
eaa94edfac | ||
|
|
1b30a7a873 | ||
|
|
59399d672f | ||
|
|
bb5c319a7f | ||
|
|
b114d352f6 | ||
|
|
ea4b27776a | ||
|
|
faa6db9c73 | ||
|
|
6b5c2c0b2e | ||
|
|
de3cb37bc1 | ||
|
|
9a69450fb6 | ||
|
|
513eac9e49 | ||
|
|
8c4f4c206b | ||
|
|
c731df2ae7 | ||
|
|
40c9bb1190 | ||
|
|
ef1c1c349d | ||
| 1c49dc25b1 | |||
| 542583efc6 | |||
| f8f5b98854 | |||
|
|
39a65add7c | ||
|
|
0feade1f5b | ||
|
|
5d45eee7a3 | ||
|
|
d31a809fa8 | ||
|
|
750d90ee0b | ||
|
|
70537847bc | ||
|
|
4688f234d9 | ||
|
|
5599edfcd0 | ||
|
|
73c2d1d41e | ||
|
|
2fad680490 | ||
|
|
85da122590 | ||
|
|
62e977c703 | ||
|
|
a1e0e19412 | ||
|
|
b10fd72008 | ||
|
|
a78056c898 | ||
|
|
07c261d74c | ||
|
|
fc28d78357 | ||
|
|
6bee02dfa5 | ||
|
|
e19c850214 | ||
| 9bdd0a23d3 | |||
| 08b8a52d83 | |||
|
|
11fb6f908c | ||
|
|
27198cc35b | ||
|
|
844694d638 | ||
| 695a91ac1c | |||
| 4fd66fcc05 | |||
| aa193f08cb | |||
|
|
3692361551 | ||
|
|
008e14ec57 | ||
|
|
248732b085 | ||
|
|
033950babe | ||
|
|
dbd1651a37 | ||
|
|
25ad9799f8 | ||
|
|
747a3b0ebc | ||
|
|
27e43f3c97 | ||
| 516ad19db7 | |||
| d1af68d60a | |||
|
|
eb1de5abb3 | ||
|
|
cdcb18c02c | ||
| f11805c65a | |||
| ce21acba93 | |||
| a48e517e76 | |||
|
|
f837542797 | ||
|
|
74b43e431b | ||
|
|
c20ef9d00c | ||
|
|
a7b5cc1685 | ||
|
|
5971ab56c0 | ||
|
|
7ff2c2095a | ||
|
|
9613d2b5b2 | ||
|
|
94b9977ef6 | ||
|
|
315254b6d5 | ||
|
|
31c2ca4e8b | ||
|
|
af5f178476 | ||
|
|
0aae85e94d | ||
|
|
4c25e4a5a3 | ||
|
|
87fd5b9a93 | ||
|
|
e81049c332 | ||
|
|
28bc0f2f0e | ||
|
|
4126e0b24d | ||
|
|
e4cdebfe40 | ||
|
|
55f4e5626e | ||
| a0a751c661 | |||
| 9aa3a2f889 | |||
|
|
43e3a79124 | ||
|
|
6b0d26ed6e | ||
|
|
9b29939bfe | ||
|
|
0add8bc412 | ||
|
|
618b9bab1f | ||
|
|
226e183f52 | ||
|
|
b78832875c | ||
|
|
c4c4753403 | ||
| 3014414c97 | |||
| 6a5a0930e9 | |||
|
|
f813384a6e | ||
|
|
a720fba84e | ||
|
|
2b7db933d9 | ||
|
|
ba5b1657a5 | ||
|
|
30109fdb79 | ||
|
|
83226f006c | ||
|
|
a5e21c93b3 | ||
|
|
1989c22562 | ||
|
|
882740592c | ||
|
|
0c995054a2 | ||
|
|
6780c0fbb1 | ||
|
|
7101daeb90 | ||
|
|
0d0de45a25 | ||
|
|
28b6153ab0 | ||
|
|
7a4fc0736d | ||
| fb1bfc353c | |||
| cc0127f195 | |||
|
|
f4043d6f61 | ||
|
|
9912f310ec | ||
|
|
1df7b62fe6 |
2
.env.dev
@@ -7,5 +7,7 @@ VITE_APP_BASE_URL = 'https://develop.api.aida.com.hk'
|
||||
# VITE_APP_BASE_URL = 'https://www.api.aida.com.hk'
|
||||
# 徐佩
|
||||
# VITE_APP_BASE_URL = 'http://192.168.31.118:5567'
|
||||
# 李天祥
|
||||
# VITE_APP_BASE_URL = 'http://192.168.31.82:5567'
|
||||
# 海波
|
||||
# VITE_APP_BASE_URL = 'http://192.168.31.34:5567'
|
||||
|
||||
@@ -5,4 +5,3 @@ VITE_USER_NODE_ENV = 'development'
|
||||
VITE_APP_BASE_URL = 'https://develop.api.aida.com.hk'
|
||||
|
||||
# VITE_APP_BASE_URL = 'http://localhost:22170'
|
||||
|
||||
|
||||
33
.prettierrc.js
Normal 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',
|
||||
};
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
<link rel="stylesheet" href="/css/sloganFamily.css">
|
||||
<link rel="stylesheet" href="/css/pingfang.css">
|
||||
<link rel="stylesheet" href="/css/fonts/fontFamily.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
BIN
public/css/fonts/ARIAL.ttf
Normal file
BIN
public/css/fonts/ARIALBD.ttf
Normal file
BIN
public/css/fonts/ArialMdm.ttf
Normal file
BIN
public/css/fonts/InstrumentSans-Bold.ttf
Normal file
BIN
public/css/fonts/InstrumentSans-Regular.ttf
Normal file
BIN
public/css/fonts/Poppins-Medium.ttf
Normal file
BIN
public/css/fonts/Poppins-Regular.ttf
Normal file
BIN
public/css/fonts/Poppins-SemiBold.ttf
Normal file
41
public/css/fonts/fontFamily.css
Normal file
@@ -0,0 +1,41 @@
|
||||
/* 字体定义 */
|
||||
@font-face {
|
||||
font-family: 'Arial';
|
||||
src: url('./fonts/ARIAL.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'ArialBold';
|
||||
src: url('./fonts/ARIALBD.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'ArialMedium';
|
||||
src: url('./fonts/ArialMdm.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
src: url('./fonts/Poppins-Regular.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'PoppinsMedium';
|
||||
src: url('./fonts/Poppins-Medium.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'PoppinsBold';
|
||||
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');
|
||||
}
|
||||
BIN
public/image/events/award-poster-zh.gif
Normal file
|
After Width: | Height: | Size: 15 MiB |
BIN
public/image/events/award-poster.gif
Normal file
|
After Width: | Height: | Size: 15 MiB |
17
src/assets/icons/CBrush2.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="201.000000pt" height="200.000000pt" viewBox="0 0 201.000000 200.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
|
||||
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M1654 1990 c-26 -8 -153 -110 -162 -129 -1 -3 81 -89 183 -191 l185
|
||||
-185 56 55 c30 30 62 67 70 82 22 41 18 123 -8 176 -29 57 -126 158 -169 176
|
||||
-47 20 -118 27 -155 16z"/>
|
||||
<path d="M868 1243 c-525 -527 -498 -492 -568 -725 -54 -179 -59 -219 -30
|
||||
-248 19 -19 29 -21 64 -16 113 18 361 105 431 152 28 18 251 235 498 481 l447
|
||||
448 -188 188 -187 187 -467 -467z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 799 B |
17
src/assets/icons/CEraser2.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
|
||||
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M757 1273 c-214 -214 -402 -406 -418 -427 -32 -43 -37 -82 -15 -124
|
||||
23 -44 327 -338 376 -363 l45 -24 463 -3 462 -3 0 46 0 45 -342 0 -343 0 342
|
||||
343 c187 188 346 352 352 364 16 32 13 74 -7 105 -9 14 -110 116 -224 227
|
||||
l-207 201 -47 0 -48 0 -389 -387z m207 -342 l209 -209 -128 -131 c-76 -78
|
||||
-144 -138 -169 -151 -51 -24 -106 -26 -149 -4 -41 20 -337 314 -337 334 0 13
|
||||
347 370 360 370 3 0 99 -94 214 -209z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 847 B |
1
src/assets/icons/CFile.svg
Normal 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 |
20
src/assets/icons/CMarquee.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
|
||||
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M250 1545 l0 -205 80 0 80 0 0 -340 0 -340 -80 0 -80 0 0 -205 0
|
||||
-205 205 0 205 0 0 80 0 80 340 0 340 0 0 -80 0 -80 205 0 205 0 0 205 0 205
|
||||
-80 0 -80 0 0 340 0 340 80 0 80 0 0 205 0 205 -205 0 -205 0 0 -80 0 -80
|
||||
-340 0 -340 0 0 80 0 80 -205 0 -205 0 0 -205z m320 0 l0 -125 -120 0 -120 0
|
||||
0 125 0 125 120 0 120 0 0 -125z m1100 0 l0 -125 -120 0 -120 0 0 125 0 125
|
||||
120 0 120 0 0 -125z m-330 -125 l0 -80 85 0 85 0 0 -340 0 -340 -85 0 -85 0 0
|
||||
-80 0 -80 -340 0 -340 0 0 80 0 80 -85 0 -85 0 0 340 0 340 85 0 85 0 0 80 0
|
||||
80 340 0 340 0 0 -80z m-770 -965 l0 -125 -120 0 -120 0 0 125 0 125 120 0
|
||||
120 0 0 -125z m1100 0 l0 -125 -120 0 -120 0 0 125 0 125 120 0 120 0 0 -125z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1,68 +1,29 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="103.000000pt" height="92.000000pt" viewBox="0 0 103.000000 92.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="_图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 184.06 163.52">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1, .cls-2 {
|
||||
fill: #333;
|
||||
}
|
||||
|
||||
<g transform="translate(0.000000,92.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M365 895 c-5 -2 -36 -6 -67 -10 -45 -5 -58 -10 -59 -23 0 -11 -2 -12
|
||||
-6 -4 -7 17 -32 15 -40 -4 -4 -11 -8 -12 -13 -4 -5 8 -13 9 -21 4 -7 -4 -22
|
||||
-9 -34 -10 -11 -1 -41 -11 -66 -21 l-47 -18 20 -63 c11 -38 23 -60 30 -56 6 4
|
||||
8 -1 3 -15 -3 -11 -3 -21 2 -21 4 0 9 -12 9 -26 1 -14 5 -28 8 -32 4 -3 33 3
|
||||
66 15 33 11 61 19 62 18 2 -2 1 -139 -2 -304 l-5 -301 309 2 309 3 -2 108 c-2
|
||||
77 2 113 11 125 10 12 10 14 1 8 -7 -4 -13 -2 -13 4 0 6 10 8 23 5 18 -5 26
|
||||
-1 36 17 9 17 10 18 6 3 -5 -16 -4 -18 6 -7 18 17 -1 34 -40 37 l-32 2 -2 151
|
||||
c-1 82 0 147 3 144 9 -12 44 -11 52 1 5 8 8 7 8 -4 0 -14 26 -28 52 -29 11 0
|
||||
39 70 33 80 -3 5 1 11 7 13 10 4 9 8 -2 16 -13 10 -8 15 12 12 8 -1 38 91 32
|
||||
98 -2 2 -10 -2 -18 -8 -9 -7 -17 -8 -20 -2 -3 5 0 11 6 14 7 2 -18 14 -56 26
|
||||
-38 12 -71 19 -74 16 -3 -3 -11 0 -18 6 -8 6 -20 9 -28 6 -8 -3 -16 -2 -18 3
|
||||
-5 15 -154 30 -286 29 -70 0 -131 -2 -137 -4z m21 -30 c-6 -18 3 -47 14 -40 4
|
||||
2 18 -7 30 -20 27 -29 17 -34 -14 -7 -20 16 -20 16 -7 -1 23 -29 66 -47 112
|
||||
-47 34 0 40 3 35 16 -5 14 -4 15 9 4 13 -11 19 -9 36 6 12 11 29 35 38 54 15
|
||||
31 20 35 58 35 24 0 43 -2 43 -5 0 -11 115 -27 121 -18 3 5 9 2 13 -7 4 -13
|
||||
14 -16 34 -12 15 2 36 1 47 -4 16 -7 14 -8 -10 -5 l-30 5 32 -14 c39 -18 39
|
||||
-19 14 -83 -15 -38 -17 -52 -8 -61 9 -9 8 -11 -7 -5 -16 6 -18 4 -12 -19 3
|
||||
-14 2 -29 -4 -32 -6 -3 -7 1 -4 9 6 16 -9 20 -73 19 -12 0 -20 4 -17 8 3 5 -4
|
||||
9 -15 9 -20 0 -21 -5 -21 -151 l0 -151 -27 8 c-16 4 -38 7 -50 7 -19 -1 -21 2
|
||||
-13 17 15 28 12 57 -6 57 -11 0 -15 -8 -12 -27 4 -24 1 -27 -23 -26 -15 1 -25
|
||||
4 -22 9 2 4 -2 7 -10 7 -9 0 -14 -10 -14 -25 0 -13 3 -22 8 -19 5 3 6 -1 3 -9
|
||||
-2 -7 0 -25 5 -40 8 -20 7 -25 -3 -21 -7 3 -13 -2 -13 -11 0 -12 7 -15 26 -11
|
||||
14 3 21 3 14 0 -7 -3 -9 -12 -6 -20 3 -7 10 -11 16 -7 5 3 7 1 4 -4 -9 -14 3
|
||||
-74 12 -68 4 2 7 -6 7 -18 -1 -32 32 -34 44 -3 5 14 7 34 5 46 -3 13 0 18 7
|
||||
14 8 -5 9 -1 5 10 -4 9 -3 15 2 12 5 -3 12 1 15 10 3 8 2 12 -4 9 -6 -3 -10
|
||||
-1 -10 4 0 13 3 13 24 5 13 -5 16 -24 16 -103 0 -63 4 -102 13 -111 10 -12 9
|
||||
-12 -4 -2 -13 10 -88 12 -300 10 l-284 -3 3 303 3 302 -26 0 c-14 0 -25 -4
|
||||
-25 -10 0 -5 -7 -6 -17 -3 -9 4 -14 2 -10 -3 4 -6 -6 -9 -24 -6 -28 4 -41 -11
|
||||
-19 -21 6 -3 5 -4 -2 -3 -7 2 -13 11 -13 22 -1 32 -25 104 -35 104 -5 0 -6 7
|
||||
-3 17 4 10 2 14 -5 9 -7 -4 -10 2 -8 17 3 29 14 47 29 47 7 0 3 -8 -8 -17
|
||||
l-20 -16 20 8 c11 4 28 10 37 12 10 3 18 9 18 13 0 4 6 7 12 7 22 -2 158 26
|
||||
158 32 0 3 20 6 45 5 25 0 45 3 45 8 0 4 3 8 6 8 3 0 4 -7 0 -15z m237 -5 c-3
|
||||
-9 1 -8 11 4 9 11 16 15 16 9 0 -6 -7 -16 -15 -23 -8 -7 -15 -9 -15 -4 0 10
|
||||
-29 -28 -30 -40 0 -4 8 -5 17 -2 15 6 15 4 -2 -14 -22 -24 -37 -26 -28 -4 5
|
||||
14 3 15 -15 5 -26 -14 -77 -14 -103 -1 -10 6 -27 27 -38 48 l-19 37 113 0 c95
|
||||
0 112 -2 108 -15z"/>
|
||||
<path d="M346 641 c-3 -5 1 -12 10 -15 23 -9 36 -7 29 4 -3 6 1 7 9 4 9 -3 16
|
||||
-1 16 5 0 13 -56 15 -64 2z"/>
|
||||
<path d="M440 640 c0 -16 33 -26 38 -12 2 7 8 10 13 6 5 -3 9 0 9 5 0 6 -13
|
||||
11 -30 11 -16 0 -30 -5 -30 -10z"/>
|
||||
<path d="M530 641 c0 -12 37 -24 50 -16 20 12 10 25 -20 25 -16 0 -30 -4 -30
|
||||
-9z"/>
|
||||
<path d="M620 641 c0 -12 37 -24 50 -16 20 12 10 25 -20 25 -16 0 -30 -4 -30
|
||||
-9z"/>
|
||||
<path d="M310 593 c0 -20 5 -30 16 -30 10 0 14 8 12 25 -4 37 -28 40 -28 5z"/>
|
||||
<path d="M697 613 c-13 -13 -7 -50 8 -50 10 0 15 10 15 29 0 27 -9 35 -23 21z"/>
|
||||
<path d="M317 534 c-4 -4 -7 -20 -7 -36 0 -35 23 -34 28 1 4 25 -10 46 -21 35z"/>
|
||||
<path d="M692 503 c4 -39 28 -42 28 -4 0 21 -5 31 -16 31 -11 0 -14 -8 -12
|
||||
-27z"/>
|
||||
<path d="M312 415 c4 -33 22 -33 26 0 2 18 -1 25 -13 25 -12 0 -15 -7 -13 -25z"/>
|
||||
<path d="M312 329 c2 -19 8 -33 13 -31 15 3 12 55 -3 60 -10 3 -13 -5 -10 -29z"/>
|
||||
<path d="M342 278 c3 -7 19 -14 37 -16 24 -3 32 0 29 10 -3 7 -19 14 -37 16
|
||||
-24 3 -32 0 -29 -10z"/>
|
||||
<path d="M443 275 c0 -10 10 -15 29 -15 18 0 28 5 28 15 0 10 -10 15 -28 15
|
||||
-19 0 -29 -5 -29 -15z"/>
|
||||
<path d="M530 275 c0 -10 10 -15 33 -15 22 0 28 3 18 9 -11 7 -11 9 0 14 8 3
|
||||
-1 6 -18 6 -23 1 -33 -4 -33 -14z"/>
|
||||
</g>
|
||||
</svg>
|
||||
.cls-2 {
|
||||
stroke: #333;
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="cls-2" d="M35.53,54.52c-.76-.96-.84-1.16-2.06-1.12-4.02.13-16.7,6.5-19.81,4.48C12.45,52.21-1.21,21.14.68,18.17c56.98-23.47,125.74-23.63,182.7,0,1.88,2.98-11.77,34.04-12.98,39.72-3.11,2.01-15.78-4.36-19.81-4.48-1.23-.04-1.3.16-2.06,1.12v48c2.87,3.81,14.6,2.98,14.05,8.95-1.06,5.75-12.53,3.84-13.85,8.24l.34,39.85c.08.98-1.55,3.46-2.04,3.46H37.03c-2.08,0-1.5-3.59-1.5-4.5V54.52ZM41.53,47.52v108.5h101v-36c-7.66,1.58-6.82,23.41-12.7,24.86-1.18.17-2.67-.14-3.63-.79-2.98-2.02-6.47-24.26-10.3-25.81-.94-.38-4.46.95-5.31-1.9-1.01-3.4,3.85-4,4.01-5.02-.12-4.15-6.85-15.14-3.84-18.1,4.45-3.04,8.95,1.93,13.76,1.74-1.35-3.15-2.75-11.61,2.46-11.03s3.05,9.89,1.55,13.02l14,4.01v-53.5c0-5.17,22.25,4.69,23.94,3.46,1.7-8.98,7.54-18.42,9.64-26.94.32-1.3.79-1.48-.58-2.5-2.81-2.08-21.4-6.93-25.98-8.02-10.05-2.4-20.36-3.72-30.52-5.53-2.44.17-3.98,7.98-6.05,10.97-13.1,18.91-41.58,12.05-45.92-9.95l-2-1.02c-19.44,3.3-39.69,6.08-57.48,14.6l10.03,28.4c4.22.52,22.41-8.34,23.94-3.46ZM110.53,8.02h-37c4,20.53,33,20.53,37,0Z"/>
|
||||
<path class="cls-1" d="M89.27,117.8c-.94.96-10.2,1.42-11.25-.78-4.1-8.61,17.67-5.76,11.25.78Z"/>
|
||||
<path class="cls-1" d="M94.79,117.8c-6.42-6.54,15.35-9.39,11.25-.78-1.05,2.19-10.31,1.74-11.25.78Z"/>
|
||||
<path class="cls-1" d="M60.62,113.11c1.1-1.64,14.59-2.57,12.84,3.27-1.14,3.81-16.87,2.76-12.84-3.27Z"/>
|
||||
<path class="cls-1" d="M62.18,46.17h9.69s1.53,3.19,1.53,3.19c-1.22,4.73-18.53,3.97-11.22-3.19Z"/>
|
||||
<path class="cls-1" d="M112.19,46.18l10.91.77.34,3.47c-.87,2.65-12.93,2.92-12.96-1.9l1.7-2.34Z"/>
|
||||
<path class="cls-1" d="M56.23,84.18c6.63-1.86,6.45,14.76-.7,11.35-2.24-1.07-2.61-10.42.7-11.35Z"/>
|
||||
<path class="cls-1" d="M89.37,46.16l.96,3.22c-.81,4.5-16.92,3.98-12.37-2.43l11.42-.79Z"/>
|
||||
<path class="cls-1" d="M94.68,46.16l11.42.79.34,3.47c-1.03,3.13-16.69,2.9-11.76-4.26Z"/>
|
||||
<path class="cls-1" d="M54.75,63.76c-1.01-.99-2.01-12.94,3.11-11.63,5.4,1.38,2.78,17.42-3.11,11.63Z"/>
|
||||
<path class="cls-1" d="M129.31,63.76c-5.88,5.78-8.51-10.25-3.11-11.63,5.12-1.31,4.12,10.64,3.11,11.63Z"/>
|
||||
<path class="cls-1" d="M59.44,68.12c1.19.8,3.28,13.19-3.02,12.43l-2.23-1.77c-.57-2.83-.34-14.39,5.25-10.66Z"/>
|
||||
<path class="cls-1" d="M59.44,111.92c-5.59,3.73-5.82-7.83-5.25-10.66l2.23-1.77c6.3-.76,4.22,11.63,3.02,12.43Z"/>
|
||||
<path class="cls-1" d="M129.32,79.79c-5.12,5.18-7.81-7.81-4.7-11.67l3.28.02,1.48,1.52c-.35,2.45,1.51,8.54-.07,10.13Z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 2.6 KiB |
BIN
src/assets/images/award/apply_bg.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
3
src/assets/images/award/arrow_down.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="22" height="12" viewBox="0 0 22 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 1L11 11L21 1" stroke="#585858" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 212 B |
BIN
src/assets/images/award/arrow_right.png
Normal file
|
After Width: | Height: | Size: 327 B |
BIN
src/assets/images/award/banner.mp4
Normal file
BIN
src/assets/images/award/banner_chinese.mp4
Normal file
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 492 KiB |
BIN
src/assets/images/award/certification_bg.png
Normal file
|
After Width: | Height: | Size: 231 KiB |
BIN
src/assets/images/award/checked.png
Normal file
|
After Width: | Height: | Size: 913 B |
4
src/assets/images/award/close.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.6144 18.7388L5.16736 4.29184C4.95412 4.0786 4.60838 4.0786 4.39514 4.29184C4.18189 4.50509 4.18189 4.85082 4.39514 5.06407L18.8421 19.5111C19.0554 19.7243 19.4011 19.7243 19.6144 19.5111C19.8276 19.2978 19.8276 18.9521 19.6144 18.7388Z" fill="#232323"/>
|
||||
<path d="M5.15908 19.5378L19.6061 5.09079C19.8193 4.87755 19.8193 4.53181 19.6061 4.31857C19.3928 4.10533 19.0471 4.10533 18.8339 4.31857L4.38685 18.7656C4.17361 18.9788 4.17361 19.3246 4.38685 19.5378C4.6001 19.751 4.94583 19.751 5.15908 19.5378Z" fill="#232323"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 636 B |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 137 KiB |
BIN
src/assets/images/award/criteria_1.png
Normal file
|
After Width: | Height: | Size: 339 KiB |
BIN
src/assets/images/award/criteria_2.png
Normal file
|
After Width: | Height: | Size: 443 KiB |
BIN
src/assets/images/award/criteria_3.png
Normal file
|
After Width: | Height: | Size: 184 KiB |
BIN
src/assets/images/award/criteria_4.png
Normal file
|
After Width: | Height: | Size: 280 KiB |
BIN
src/assets/images/award/criteria_bg.png
Normal file
|
After Width: | Height: | Size: 400 KiB |
BIN
src/assets/images/award/desmond.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
src/assets/images/award/diego.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
src/assets/images/award/expired.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
9
src/assets/images/award/facebook.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<rect width="20" height="20" rx="2.79453" fill="url(#pattern0_226_198)"/>
|
||||
<defs>
|
||||
<pattern id="pattern0_226_198" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image0_226_198" transform="scale(0.0104167)"/>
|
||||
</pattern>
|
||||
<image id="image0_226_198" width="96" height="96" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAKcUlEQVR4AeydSewlRR3H3xtHUXAZdz2YGQ+aeDDiCYgCMxwENFGMMdF4cDx48gB6VOOuiR4UPWlkGSAsBxK2BMI6wxYgLMOSEJYAQ9jXGbZh58/n0/Oq6ffmLd3Vr1/369f/1PfV0lW/qvr+flVdr7pe/9f1Cv6tra1tAseDU8FDYDdYVWffd9L584CcbCpIZy+3AqhgK9hOBQ+BE8FWYIUb8FfV2feD6fxxQE40SBXyU+K53EwFQPpmIOmnInEz6Nx0BlTINjkDMxUxUQEU3gDUqlavpU+vtrs6yoCcqYh/waMjZfR6Eh+rAApYWOKPT3J1H2UYOIHCTktySnDY7aeADPkOpeHcXSyWAcnfPuB2SMaQAsjgUNHyLTCUsYuUZkBOXS3JcSpsSAGk/gGYEa9zFTDgrPL7rNxUAVi/y8puzs+yU034BLhOV5OpAqhrSDPEO1cdAy7pE+mJAtCI1r86U0/S9Vo/3E1IviMkCqApqzj1rNHvebl3EFRUnkbfW4f1a/neHJDRuUgGipJvNe4wbHQEfM/YCmCUpP6gz1rvs4QfBXeDO8GN4HpwNdgxgiuJXwvM8zD+XvA+EOQRzO2OUwHpHTl3seXLOEp+tgevEbkX3ADOBaeD/4C/gz8O4PJc/I74b8Ffwb/BNeAZEOsOVgFOQbEClqGc5Gudwva+zccT4H5wIXBF8n/8k8Apot/vnw0uAtvBDnD1ANfi3wguBeeQ92LwJIh1m9uuAMkfJedVErT2M/G1dBXgtHI7ca35Zfy8bg8Z3wCxboMKGPpqHCupoeW0eqEiXqSNEn0G/n/B/7DiK8Gt4FHwNNgL3uTaRMeiRXnhugqYmj9knOAnCphwrVXJr9Abb6qn4Z8FbgLeePGKORSkMkOh9xPQiPHiXKnCcVVWXipLkCuc56jR6cZ53rn9Okh8EbxFeuJGrDpJy/nh6idn1vHZ2qiAbE93E7kMuLrZmSWdtNSRnlVamr6IwAIVUHl3AonO0Ya9mUr8yRB8BdgVWoDFN6bfjWlIICfSl/BsUW+4flnS+h/IXjCMMpyaDNaOtihAqxeB0FsInA0uhOys5WfzcLl+1xYFBCb9kuXUcx8JD0J+eqMl3iM+OlJMrhVtUECW1Kdh83JwPrgLJG6ecz6ysqNIhSd1xH60QQHZvr9E5A7ghpqbZAR7Wn5Vc74jrJTsNigga5H39Ho992ueZLqRHKL53IhlTyyE3OyI+xgZ/TKGF+faoAB7rhVq8Y8QeQEUcnnJHyP046R9AES7tijgeRi4DdwM0t3JPMSaR6sWlO0RPwB8AXwVHAGOBFuAD1AOxz8UHAN+TP5jwOdAtGuLAtzhfBwWJD/dnQykkj7WQWJ2+pJ8404r36TAT8BvgIcV/oQfng34LODXxH2MeyT+p0G0a4sCvPk6/wuXobkIUUEik/nrhH8AfgF+Dr4FtgAVcgS+4aPwDweHgI3gQOD9JntvIGmfm/XZFgVIgN9+heFZ/R66zkhw2vksiZJ67MD/BH5e56acoydv/jRfWxTgDfgxrHk3iNmfl3wt22nHeX19ytC+wCzrjiJf0W1RgFav9dunGDjvf42CXwGj5JPUiybYwtPQFgVo9bnn/jGEuJx0/nfaqYzsMfXm/4nSuMINSpM0Edsky5b6QhVbcVtGQGz/Qzl5yCpg1pwfypX2rbi0kBYIkHC/TYeuOCJCuFJ/mRUgaYGc6GXgQICEi0G0l5Ud0irxl1kBWUJcuZTpi+QrIytzIeEyja6igVpediqYVoekhesuQV0JhXhR32Ws36ZDuazskDbNt93Trk+81jQF2NCYznj6Id0D6imlGFSeSixW6r3cGk1Muxu3DNXynM89SOXJYw9Q+XD9KvrqjwfDSWXTPJ18Keme0bwE3404vChnWc95eopCmR5d9OBtqC/4tuEKavCQ1058z5h6uNc223aSirkmjgB74Kk1j4b7Q3F3Hz2R7K5k2JH0lLI7lV4zj3k912nZGPgoU3L/SeG/AevzNHSoL/jW607oP8jjQV6V4PFEonGuqQrQsi5hX+cc4EnkG/B90uVJ5YCrBmk34T8M9rKpth7ktkTy+gOV9ZR9BewC1nMNvrLDyehQn76n6m7muqeqt0G5I0FjIRjnmqoA52Tn9aK9ehtycs/F5HXujn2w7j3H+4ZtLdrONH9TFWDn0keLWGouq4bQ3OQHBmLKDNrjvF+4vlBv8JuqANuVbg3EkBQ6WJF/AHLdQRVpO0kr7Oxo4UILKKDFa2ELqCqqChXwGUoKwwTjXFMVENebxZXSOD5EdaLUN+hOAbAY4bT6T1LuU6A7lgIJi3ZhBHyQig3jxbk5joC4BixpKUeAx1GE4ehudAqIo06rP4iiwjDBONdUBbi+Tr8gDdbdcT2sppRLT+8BrR0BKsAt4mroi5A6YgQuk53/RStXQXbqI4GnvF/ERkgKxSf65ge5ZoGRNnwYoZ4lso2tnIIc4n7LpJ+FnJtrWmeRQkXzK9v1/0cNlEUu7ZetJKL85ylzLNb5Q3A0OAx4UtkTygFHkRZOK28kfCBWWnQzbs0ylD0IKOMQfGUqO5yItj7r/gbXTPe86Hdp35dBaddUBXhz8yDsr+ih++9/wXd/3mcCwtPKPgvwmYCnlD04axmyRTm/UHnS2Xd8hvp8BmBdwvCfkezzAOv8DmGNBK+ca5oC3B725uvyzre4HEr3JNdzmyrEV+sITyd77Wiu/wh8G5Q5py+ZHspVlmdDD0OedVhXgPWrJNvzJa47/7tSc8FANM41TQG2J+am5gtHymwJeM/xeGJRFm1vzD0krUcBaaQhgTwd0upEaLIKkMQQL+pbNnvTz8qeJqs/7WKea01UQJ52qyQR8vpUyukrxIv6Eq6MUC4rO6RV4i+rAkbJcC4eTSsSVwFlZRSpL83bFgWkHVq2QKeAmjXWKaBTQM0M1Fx9NwI6BdTMQM3VdyOgU0DNDNRc/XKOgJpJm2f1nQLmyWaErE4BEaTNs0ingHmyGSGrU0AEafMsogJK/cRmno1ZQVl7OgXUq/VdKsBfANbbjNWtPVGA/zlidSmot+c7HAEX1NuGla79gnX9ft+XW3fT0OLt4Ha5dwRYtf/aQ38KuktzZsAfmO97VQGa8EfHjoQ519GJm8CAPwpPjD6MAPP9zI8OC2HAY5ZJRakCGAXeB5JhkVzpPqpi4ES4TqzfClIFGAEeQu2WpRBRkXOal+NU/JAC0IzbEt/nqhnxOjdHBuR0y4DjVOyQAkwlQ5KRsD5eY52n2ULjYg70hrL6fT6yMrKyuVTaOatI/n6c7qcAqxoowReZLss9wZ80je2L/ckBFaCMHFkLZ5HDseQraWKjUcIe8EsyuTraT3Ok1+0kLbThKQK+uQovyr1OKV/ahJe4rOwkIeJDzjbLIXBqHytiogJCbgpvA18kriIcSgQb4zwRnby4mxb5PwTwopxlfUmUilBmlJBBIVeTW+UM+CavQfJ4b6YCQjGEqQinJZXhT3n8T0UqZKJ2Q9kKfa1ey3UESF5sVZb1lWe+/cp3FaVyZgTsu5buF1k52QRPTjfpMnNG+d67AAAA///vdBW6AAAABklEQVQDAJAXvRKwmZ6CAAAAAElFTkSuQmCC"/>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
BIN
src/assets/images/award/first_bg.png
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
src/assets/images/award/form_bg.png
Normal file
|
After Width: | Height: | Size: 930 KiB |
BIN
src/assets/images/award/grand_bg.png
Normal file
|
After Width: | Height: | Size: 262 KiB |
BIN
src/assets/images/award/gregory.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
src/assets/images/award/jae.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
src/assets/images/award/judges_bg.png
Normal file
|
After Width: | Height: | Size: 198 KiB |
10
src/assets/images/award/linkdin.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_226_195)">
|
||||
<path d="M-0.00195312 1.4335C-0.00195312 0.641696 0.656004 0 1.46782 0H18.5422C19.354 0 20.0119 0.641696 20.0119 1.4335V18.5804C20.0119 19.3722 19.354 20.0139 18.5422 20.0139H1.46782C0.656004 20.0139 -0.00195312 19.3722 -0.00195312 18.5804V1.4335ZM6.18109 16.7541V7.71661H3.17776V16.7541H6.18109ZM4.68005 6.482C5.72703 6.482 6.37873 5.78902 6.37873 4.92092C6.35996 4.03405 5.72828 3.35983 4.70006 3.35983C3.67185 3.35983 3.00013 4.0353 3.00013 4.92092C3.00013 5.78902 3.65183 6.482 4.66003 6.482H4.68005ZM10.8193 16.7541V11.7069C10.8193 11.4367 10.8393 11.1665 10.9194 10.9739C11.1358 10.4347 11.6299 9.87561 12.4605 9.87561C13.5475 9.87561 13.9815 10.7037 13.9815 11.9195V16.7541H16.9848V11.5705C16.9848 8.79361 15.5038 7.50271 13.5274 7.50271C11.9338 7.50271 11.2196 8.37832 10.8193 8.995V9.02627H10.7993L10.8193 8.995V7.71661H7.81723C7.85475 8.5647 7.81723 16.7541 7.81723 16.7541H10.8193Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_226_195">
|
||||
<rect width="20.0139" height="20.0139" rx="1.55252" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/images/award/point.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
src/assets/images/award/prizes_bg.png
Normal file
|
After Width: | Height: | Size: 4.3 MiB |
BIN
src/assets/images/award/progress.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
src/assets/images/award/qrcode.jpg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
src/assets/images/award/second_bg.png
Normal file
|
After Width: | Height: | Size: 253 KiB |
BIN
src/assets/images/award/selection_bg.png
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
src/assets/images/award/successful.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
9
src/assets/images/award/tiktok.svg
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
src/assets/images/award/tim.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 61 KiB |
BIN
src/assets/images/award/upload.png
Normal file
|
After Width: | Height: | Size: 225 KiB |
BIN
src/assets/images/award/upload_video_icon.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
src/assets/images/award/vincenzo.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
12
src/assets/images/award/weichat.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_226_203)">
|
||||
<path d="M20 17C20 18.6569 18.6569 20 17 20H3C1.34307 20 0 18.6569 0 17V3C0 1.34313 1.34313 0 3 0H17C18.6569 0 20 1.34313 20 3V17Z" fill="white"/>
|
||||
<path d="M13.3881 7.45803C11.7739 7.54236 10.3702 8.03169 9.2306 9.13723C8.0792 10.2542 7.55366 11.6228 7.69726 13.3195C7.06633 13.2414 6.49166 13.1554 5.91373 13.1067C5.71413 13.0899 5.47726 13.1138 5.3082 13.2092C4.747 13.5258 4.20906 13.8834 3.57133 14.282C3.68833 13.7528 3.76406 13.2894 3.89813 12.8437C3.99673 12.5161 3.95106 12.3338 3.64926 12.1204C1.71153 10.7523 0.894728 8.70496 1.50599 6.59703C2.07153 4.64703 3.46033 3.46443 5.3474 2.84796C7.92313 2.00663 10.8177 2.86483 12.3839 4.90976C12.9495 5.64843 13.2964 6.47749 13.3881 7.45803ZM5.95893 6.80123C5.9738 6.41569 5.63973 6.06836 5.24293 6.05676C4.83666 6.04483 4.50253 6.35529 4.49066 6.75563C4.47866 7.16136 4.789 7.48649 5.1982 7.49689C5.60386 7.50716 5.94393 7.19629 5.95893 6.80123ZM9.835 6.05649C9.43673 6.06383 9.1002 6.40303 9.10726 6.79009C9.11453 7.19129 9.44466 7.50516 9.8542 7.50023C10.2648 7.49529 10.5762 7.17809 10.5723 6.76843C10.5689 6.36629 10.2403 6.04909 9.835 6.05649Z" fill="#232323"/>
|
||||
<path d="M17.0146 17.5216C16.5035 17.294 16.0346 16.9525 15.5354 16.9004C15.0381 16.8484 14.5154 17.1353 13.9951 17.1885C12.4104 17.3506 10.9907 16.909 9.82001 15.8264C7.59355 13.767 7.91168 10.6094 10.4876 8.92184C12.777 7.42197 16.1345 7.92197 17.7487 10.0031C19.1573 11.8191 18.9917 14.2298 17.2721 15.7554C16.7745 16.1969 16.5955 16.5602 16.9148 17.1423C16.9737 17.2498 16.9804 17.3858 17.0146 17.5216ZM11.1963 11.8883C11.5217 11.8886 11.7897 11.634 11.802 11.3126C11.815 10.9723 11.5413 10.6869 11.2006 10.6855C10.8632 10.684 10.5806 10.9734 10.5924 11.3086C10.6035 11.6288 10.8733 11.8879 11.1963 11.8883ZM14.9471 10.6868C14.6314 10.6846 14.3631 10.9431 14.3502 11.2621C14.3365 11.6032 14.6017 11.8834 14.9393 11.8842C15.2658 11.8854 15.5239 11.6384 15.5357 11.3134C15.5483 10.9715 15.2831 10.6892 14.9471 10.6868Z" fill="#232323"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_226_203">
|
||||
<rect width="20" height="20" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
4
src/assets/images/award/xiaohongshu.svg
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
src/assets/images/canvas/add.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
src/assets/images/canvas/remove.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src/assets/images/canvas/shubiao-l.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
src/assets/images/canvas/shubiao-r.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
@@ -1,93 +1,124 @@
|
||||
{
|
||||
"eventsList": [
|
||||
"eventsList": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face!",
|
||||
"imgUrl": "/image/events/workshop-En.jpg"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"title":"Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face!",
|
||||
"imgUrl": "/image/events/workshop-En.jpg"
|
||||
},{
|
||||
"id": 2,
|
||||
"title":"AiDA X SFT AI Fashion Award 2024",
|
||||
"title": "AiDA X SFT AI Fashion Award 2024",
|
||||
"imgUrl": "/image/events/Fashion-Award-2024.png"
|
||||
}
|
||||
],
|
||||
"eventsItem":[
|
||||
{
|
||||
"id":1,
|
||||
"title":"Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face!",
|
||||
"imgUrl": "/image/events/workshop-En.jpg",
|
||||
"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",
|
||||
"textList":[
|
||||
{
|
||||
"paragraph":[
|
||||
{
|
||||
"text":"With the aim of inspiring students to innovate in fashion design using AI, Code-Create and The Hong Kong Polytechnic University School of Fashion and Textiles (SFT) have jointly launched the 'AiDA X SFT AI Fashion Award 2024'. This competition provides students with valuable practical AiDA experience, laying the foundation for the future fashion design industry and positioning them as pioneers in AI fashion."
|
||||
}
|
||||
]
|
||||
},{
|
||||
"paragraph":[
|
||||
{
|
||||
"text":"The competition is open to all SFT students, with the winners having the chance to win cash prizes (up to 20,000 HKD), internship opportunity at BESFXXK (will work with the renowned designer, Mr Jae Hyuk Lim, for the BESFXXK collection, that will be featured at NY Fashion Week and Paris Fashion Week) and more surprises! Scan the QR code to learn more."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"eventsItem": [
|
||||
{
|
||||
"id": 3,
|
||||
"title": "AiDA Global Design Awards 2026",
|
||||
"imgUrl": "/image/events/award-poster.gif",
|
||||
"textList": [
|
||||
{
|
||||
"paragraph": [
|
||||
{
|
||||
"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 Code‑Create, 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": "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."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face!",
|
||||
"imgUrl": "/image/events/workshop-En.jpg",
|
||||
"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",
|
||||
"textList": [
|
||||
{
|
||||
"paragraph": [
|
||||
{
|
||||
"text": "With the aim of inspiring students to innovate in fashion design using AI, Code-Create and The Hong Kong Polytechnic University School of Fashion and Textiles (SFT) have jointly launched the 'AiDA X SFT AI Fashion Award 2024'. This competition provides students with valuable practical AiDA experience, laying the foundation for the future fashion design industry and positioning them as pioneers in AI fashion."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"paragraph": [
|
||||
{
|
||||
"text": "The competition is open to all SFT students, with the winners having the chance to win cash prizes (up to 20,000 HKD), internship opportunity at BESFXXK (will work with the renowned designer, Mr Jae Hyuk Lim, for the BESFXXK collection, that will be featured at NY Fashion Week and Paris Fashion Week) and more surprises! Scan the QR code to learn more."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,93 +1,124 @@
|
||||
{
|
||||
"eventsList": [
|
||||
{
|
||||
"id": 1,
|
||||
"title":"什么?只要发布设计作品就有机会来香港与大佬面对面交流?!",
|
||||
"imgUrl": "/image/events/workshop-Cn.jpg"
|
||||
},{
|
||||
"id": 2,
|
||||
"title":"AiDA X SFT AI时尚设计比赛2024",
|
||||
"imgUrl": "/image/events/Fashion-Award-2024.png"
|
||||
}
|
||||
"eventsList": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "什么?只要发布设计作品就有机会来香港与大佬面对面交流?!",
|
||||
"imgUrl": "/image/events/workshop-Cn.jpg"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "AiDA X SFT AI时尚设计比赛2024",
|
||||
"imgUrl": "/image/events/Fashion-Award-2024.png"
|
||||
}
|
||||
],
|
||||
"eventsItem":[
|
||||
{
|
||||
"id":1,
|
||||
"title":"什么?只要发布设计作品就有机会来香港与大佬面对面交流?!",
|
||||
"imgUrl": "/image/events/workshop-Cn.jpg",
|
||||
"textList":[
|
||||
{
|
||||
"paragraph":[
|
||||
{
|
||||
"text":"🎨这是一趟艺术巅峰之旅!AiDA Workshop!"
|
||||
}
|
||||
]
|
||||
},{
|
||||
"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 系列,该系列将在纽约时装周和巴黎时装周上展出)及更多惊喜哦!扫描二维码获取更多比赛信息。"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
"eventsItem": [
|
||||
{
|
||||
"id": 3,
|
||||
"title": "AiDA全球设计奖 2026",
|
||||
"imgUrl": "/image/events/award-poster-zh.gif",
|
||||
"textList": [
|
||||
{
|
||||
"paragraph": [
|
||||
{
|
||||
"text": "秉承推动 AI 赋能创意设计的初衷,Code‑Create 举办了「AiDA 全球设计大奖 2026」,面向来自香港、中国、新加坡、韩国及全球的设计师,鼓励大家探索 AI 与时尚设计的无限可能,突破传统界限,释放科技与想象力的创新潜能。点击“查看详情”按钮获取更多比赛信息,抓住成为 AI 时尚先锋的机会吧!"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"paragraph": [
|
||||
{
|
||||
"text": "参赛者将有机会赢取总奖金 9,000 美元,作品还将获得国际媒体展示机会,并与全球设计师和行业领袖建立联系。入围决赛者将受邀参加在香港举办的 专属颁奖典礼,主办方提供差旅支持,让设计师在国际舞台展示才华、拓展人脉,并共同庆祝创意成果。"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"title": "什么?只要发布设计作品就有机会来香港与大佬面对面交流?!",
|
||||
"imgUrl": "/image/events/workshop-Cn.jpg",
|
||||
"textList": [
|
||||
{
|
||||
"paragraph": [
|
||||
{
|
||||
"text": "🎨这是一趟艺术巅峰之旅!AiDA Workshop!"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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 系列,该系列将在纽约时装周和巴黎时装周上展出)及更多惊喜哦!扫描二维码获取更多比赛信息。"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2488,3 +2488,25 @@ textarea:focus {
|
||||
background: #ffffff;
|
||||
color: #000;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
.flex-center {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
.space-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.flex-1{
|
||||
flex: 1;
|
||||
}
|
||||
@@ -2406,3 +2406,25 @@ textarea:focus{
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
.flex-center {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
.space-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.flex-1{
|
||||
flex: 1;
|
||||
}
|
||||
@@ -148,6 +148,7 @@
|
||||
total: total,
|
||||
showQuickJumper: true,
|
||||
bordered: false,
|
||||
showTotal: (total) => `Total Transaction: ${total}`
|
||||
}"
|
||||
>
|
||||
<template #bodyCell="{ column, text, record, index }">
|
||||
@@ -465,13 +466,16 @@ export default defineComponent({
|
||||
(rv: any) => {
|
||||
if (rv) {
|
||||
// this.dataList = rv
|
||||
// console.log('rv----',rv);
|
||||
|
||||
filter.dataList = rv.content;
|
||||
filterData.total = rv.total;
|
||||
filter.tableLoading = false;
|
||||
filterData.totalPayer = rv.content.reduce((total: number, item: any) => {
|
||||
const value = item && item.status === 'Success' ? parseFloat(item.payerTotal) : 0;
|
||||
return total + (isNaN(value) ? 0 : value);
|
||||
}, 0);
|
||||
filterData.totalPayer = rv.totalAmount;
|
||||
// filterData.totalPayer = rv.content.reduce((total: number, item: any) => {
|
||||
// const value = item && item.status === 'Success' ? parseFloat(item.payerTotal) : 0;
|
||||
// return total + (isNaN(value) ? 0 : value);
|
||||
// }, 0);
|
||||
|
||||
// this.workspaceItem.position = this.singleTypeList[0].label
|
||||
}
|
||||
|
||||
@@ -207,7 +207,8 @@ export class BackgroundSizeCommand extends Command {
|
||||
this.bgLayer = this.layers.value.find((layer) => layer.isBackground);
|
||||
|
||||
// 记录原尺寸
|
||||
this.backgroundObject = findObjectById(this.canvas, this.bgLayer.fabricObject.id).object;
|
||||
this.bgId = this.bgLayer.fabricObject?.id || this.bgLayer.fabricObjects?.[0]?.id;
|
||||
this.backgroundObject = findObjectById(this.canvas, this.bgId).object;
|
||||
|
||||
this.oldWidth = this.backgroundObject.width;
|
||||
this.oldHeight = this.backgroundObject.height;
|
||||
|
||||
@@ -50,7 +50,6 @@ export class FillGroupLayerBackgroundCommand extends Command {
|
||||
);
|
||||
this.layer = layer;
|
||||
this.parent = parent;
|
||||
console.log("==========",layer);
|
||||
if (!layer) return false;
|
||||
|
||||
if(!isUndo){
|
||||
@@ -107,7 +106,6 @@ export class FillGroupLayerBackgroundCommand extends Command {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 判断fabricObjects是否是组对象
|
||||
const firstObj = layer.fabricObjects?.[0] || null;
|
||||
// 如果没有找到第一个对象,则直接添加到当前画布
|
||||
@@ -174,8 +172,8 @@ export class FillGroupLayerBackgroundCommand extends Command {
|
||||
}
|
||||
const canvasObj = findObjectById(this.canvas, firstObj?.id)?.object;
|
||||
if (
|
||||
(canvasObj && canvasObj.type === "group") ||
|
||||
canvasObj._objects?.length > 0
|
||||
canvasObj && (canvasObj.type === "group" ||
|
||||
canvasObj._objects?.length > 0)
|
||||
) {
|
||||
this.newFill.set({
|
||||
left: 0,
|
||||
@@ -343,10 +341,6 @@ export class FillGroupLayerBackgroundCommand extends Command {
|
||||
minTop = Infinity,
|
||||
maxRight = -Infinity,
|
||||
maxBottom = -Infinity;
|
||||
console.log(
|
||||
"计算当前所有对象的边界信息:===>",
|
||||
this.originalfabricObjects.length
|
||||
);
|
||||
this.originalfabricObjects?.forEach((obj) => {
|
||||
const { object } = findObjectById(this.canvas, obj.id) || {};
|
||||
if (object) {
|
||||
|
||||
@@ -7,11 +7,11 @@ import {
|
||||
insertObjectAtZIndex,
|
||||
removeCanvasObjectByObject,
|
||||
createPatternTransform,
|
||||
getTransformScaleAngle,
|
||||
imageAddGapToCanvas,
|
||||
} from "../utils/helper";
|
||||
import { restoreFabricObject } from "../utils/objectHelper";
|
||||
|
||||
const scale = 0.3;// 默认缩放比例
|
||||
|
||||
export const FillSourceToBase64 = (source) => {
|
||||
if (source?.toDataURL) {
|
||||
return source.toDataURL?.();
|
||||
@@ -38,7 +38,6 @@ export class FillRepeatCommand extends Command {
|
||||
this.fillRepeat = options.fillRepeat;
|
||||
this.oldObjects = null;
|
||||
this.oldLocked = null;
|
||||
this.oldIsDisableUnlock = null;
|
||||
}
|
||||
|
||||
async execute() {
|
||||
@@ -63,17 +62,15 @@ export class FillRepeatCommand extends Command {
|
||||
);
|
||||
});
|
||||
image.set({
|
||||
id: object.id,
|
||||
layerId: object.layerId,
|
||||
layerName: object.layerName,
|
||||
...this.copyObjectProperties(object),
|
||||
...(fill_.originalInfo || {
|
||||
top: object.top,
|
||||
left: object.left,
|
||||
})
|
||||
});
|
||||
layer.fabricObjects = [image.toObject(["id", "layerId", "layerName"])];
|
||||
this.oldLocked = layer.locked;
|
||||
layer.locked = false;
|
||||
// this.oldLocked = layer.locked;
|
||||
// layer.locked = false;
|
||||
|
||||
this.canvas.add(image);
|
||||
this.canvas.remove(object);
|
||||
@@ -109,24 +106,35 @@ export class FillRepeatCommand extends Command {
|
||||
height: object.height,
|
||||
}
|
||||
};
|
||||
const fdObject = this.canvasManager.getFixedLayerObject();
|
||||
const bgObject = this.canvasManager.getBackgroundLayerObject();
|
||||
const tObject = fdObject || bgObject;
|
||||
const tWidth = tObject.width;
|
||||
const tHeight = tObject.height;
|
||||
|
||||
// const offsetX = object.fill?.hasOwnProperty("offsetX") ? object.fill.offsetX : tObject.width / 2;
|
||||
// const offsetY = object.fill?.hasOwnProperty("offsetY") ? object.fill.offsetY : tObject.height / 2;
|
||||
const scaleX_ = tWidth / img.width / 5;
|
||||
const scaleY_ = tHeight / img.height / 5;
|
||||
const scale_ = tWidth > tHeight ? scaleX_ : scaleY_;
|
||||
|
||||
const patternTransform = object.fill?.hasOwnProperty("patternTransform") ? object.fill.patternTransform : createPatternTransform(scale_, 0);
|
||||
const scale = getTransformScaleAngle(patternTransform).scale;
|
||||
const offsetX = tWidth / 2 - img.width * scale / 2;
|
||||
const offsetY = tHeight / 2 - img.height * scale / 2;
|
||||
const pattern = new fabric.Pattern({
|
||||
source: img,
|
||||
repeat: this.fillRepeat,
|
||||
patternTransform: object.fill?.hasOwnProperty("patternTransform") ? object.fill.patternTransform : createPatternTransform(scale, 0),
|
||||
offsetX: object.fill?.hasOwnProperty("offsetX") ? object.fill.offsetX : bgObject.width / 2, // 水平偏移
|
||||
offsetY: object.fill?.hasOwnProperty("offsetY") ? object.fill.offsetY : bgObject.height / 2, // 垂直偏移
|
||||
patternTransform,
|
||||
offsetX, // 水平偏移
|
||||
offsetY, // 垂直偏移
|
||||
});
|
||||
const rect = new fabric.Rect({
|
||||
id: object.id,
|
||||
layerId: object.layerId,
|
||||
layerName: object.layerName,
|
||||
...this.copyObjectProperties(object),
|
||||
fill_,
|
||||
});
|
||||
layer.fabricObjects = [rect.toObject(["id", "layerId", "layerName"])];
|
||||
this.oldLocked = layer.locked;
|
||||
// this.oldIsDisableUnlock = layer.isDisableUnlock;
|
||||
// layer.isDisableUnlock = true;
|
||||
// this.oldLocked = layer.locked;
|
||||
if (this.oldObjects.type === "rect") {
|
||||
rect.set({
|
||||
width: object.width,
|
||||
@@ -142,17 +150,17 @@ export class FillRepeatCommand extends Command {
|
||||
flipY: object.flipY,
|
||||
});
|
||||
} else {
|
||||
let scaleX = bgObject.scaleX || 1;
|
||||
let scaleY = bgObject.scaleY || 1;
|
||||
let scaleX = tObject.scaleX || 1;
|
||||
let scaleY = tObject.scaleY || 1;
|
||||
rect.set({
|
||||
width: bgObject.width,
|
||||
height: bgObject.height,
|
||||
top: bgObject.top - bgObject.height * scaleY / 2,
|
||||
left: bgObject.left - bgObject.width * scaleX / 2,
|
||||
width: tWidth,
|
||||
height: tHeight,
|
||||
top: tObject.top - tHeight * scaleY / 2,
|
||||
left: tObject.left - tWidth * scaleX / 2,
|
||||
scaleX,
|
||||
scaleY,
|
||||
});
|
||||
layer.locked = true;
|
||||
// layer.locked = true;
|
||||
}
|
||||
rect.set("fill", pattern);
|
||||
this.canvas.add(rect);
|
||||
@@ -181,14 +189,23 @@ export class FillRepeatCommand extends Command {
|
||||
this.canvas.remove(object);
|
||||
this.canvas.add(this.oldObjects);
|
||||
layer.fabricObjects = [this.oldObjects.toObject(["id", "layerId", "layerName"])];
|
||||
layer.locked = this.oldLocked;
|
||||
// layer.isDisableUnlock = this.oldIsDisableUnlock;
|
||||
// layer.locked = this.oldLocked;
|
||||
await this.layerManager?.updateLayersObjectsInteractivity();
|
||||
await this.layerManager?.sortLayersWithTool?.();
|
||||
this.canvas.renderAll();
|
||||
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layerId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 复制原对象的属性
|
||||
copyObjectProperties(object) {
|
||||
return {
|
||||
id: object.id,
|
||||
layerId: object.layerId,
|
||||
layerName: object.layerName,
|
||||
isPrintTrims: object.isPrintTrims,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,6 +244,10 @@ export class FillRepeatChangeCommand extends Command {
|
||||
...this.newPattern,
|
||||
});
|
||||
object.set("fill", pattern);
|
||||
if (object.globalCompositeOperation_) {
|
||||
object.globalCompositeOperation = object.globalCompositeOperation_;
|
||||
object.globalCompositeOperation_ = null;
|
||||
}
|
||||
this.canvas.renderAll();
|
||||
return true;
|
||||
}
|
||||
@@ -273,7 +294,7 @@ export class FillRepeatGapChangeCommand extends Command {
|
||||
this.oldGapY = null;
|
||||
}
|
||||
|
||||
async execute(isUndo = false) {
|
||||
async execute(isCommand = true, isUndo = false) {
|
||||
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
|
||||
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
|
||||
console.warn("图层不存在或没有 fabric 对象");
|
||||
@@ -304,21 +325,18 @@ export class FillRepeatGapChangeCommand extends Command {
|
||||
object.fill_.gapY = this.newGapY;
|
||||
}
|
||||
const image = new Image();
|
||||
image.crossOrigin = "anonymous";
|
||||
image.src = object.fill_.source;
|
||||
await image.decode();
|
||||
object.fill_.width = image.width;
|
||||
object.fill_.height = image.height;
|
||||
// 创建透明 Canvas
|
||||
const tcanvas = document.createElement('canvas');
|
||||
tcanvas.width = image.width + object.fill_.gapX;
|
||||
tcanvas.height = image.height + object.fill_.gapY;
|
||||
const ctx = tcanvas.getContext('2d');
|
||||
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
|
||||
ctx.drawImage(image, 0, 0);
|
||||
|
||||
const fill = object.get("fill");
|
||||
fill.source = tcanvas;
|
||||
fill.source = imageAddGapToCanvas(image, object.fill_.gapX, object.fill_.gapY);
|
||||
object.set("fill", new fabric.Pattern(fill));
|
||||
if (isCommand && object.globalCompositeOperation_) {
|
||||
object.globalCompositeOperation = object.globalCompositeOperation_;
|
||||
object.globalCompositeOperation_ = null;
|
||||
}
|
||||
this.canvas.renderAll();
|
||||
return true;
|
||||
}
|
||||
@@ -328,7 +346,7 @@ export class FillRepeatGapChangeCommand extends Command {
|
||||
console.warn("没有旧间隙可恢复");
|
||||
return false;
|
||||
}
|
||||
await this.execute(true);
|
||||
await this.execute(true, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -147,11 +147,11 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
}
|
||||
|
||||
// 确定源图层
|
||||
const sourceLayer = this.layerManager.getActiveLayer();
|
||||
if (!sourceLayer) {
|
||||
console.error("无法执行套索抠图:源图层无效");
|
||||
return false;
|
||||
}
|
||||
// const sourceLayer = this.layerManager.getActiveLayer();
|
||||
// if (!sourceLayer) {
|
||||
// console.error("无法执行套索抠图:源图层无效");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// 获取源图层的所有对象(包括子图层)
|
||||
// const sourceObjects = this._getLayerObjects(sourceLayer);
|
||||
@@ -225,7 +225,7 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
|
||||
const layers = this.layerManager.layers.value;
|
||||
var topLayerIndex = 0;
|
||||
layers.forEach((layer, index) => {
|
||||
if(this.originalLayer)layers.forEach((layer, index) => {
|
||||
if (layer.id === this.originalLayer.id) {
|
||||
topLayerIndex = index;
|
||||
}else if (layer.children.length > 0) {
|
||||
|
||||
@@ -280,8 +280,13 @@ export class PasteLayerCommand extends Command {
|
||||
isCut: undefined,
|
||||
serializedObjects: undefined,
|
||||
};
|
||||
|
||||
if (this.insertIndex !== undefined && this.insertIndex !== null) {
|
||||
if(this.newLayer.isPrintTrims){
|
||||
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);
|
||||
} else {
|
||||
this.layers.value.push(this.newLayer);
|
||||
@@ -3684,7 +3689,7 @@ export class ChangeFixedImageCommand extends Command {
|
||||
opacity: currentObj.opacity,
|
||||
};
|
||||
|
||||
console.log(`保存渲染顺序: z-index = ${this.previousZIndex}`);
|
||||
// console.log(`保存渲染顺序: z-index = ${this.previousZIndex}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3794,7 +3799,7 @@ export class ChangeFixedImageCommand extends Command {
|
||||
false
|
||||
);
|
||||
if (insertSuccess) {
|
||||
console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
|
||||
// console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
|
||||
} else {
|
||||
// 如果插入失败,回退到普通添加
|
||||
this.canvas.add(newImage);
|
||||
|
||||
@@ -600,7 +600,7 @@ export class ChangeFixedImageCommand extends Command {
|
||||
opacity: currentObj.opacity,
|
||||
};
|
||||
|
||||
console.log(`保存渲染顺序: z-index = ${this.previousZIndex}`);
|
||||
// console.log(`保存渲染顺序: z-index = ${this.previousZIndex}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -716,7 +716,7 @@ export class ChangeFixedImageCommand extends Command {
|
||||
false
|
||||
);
|
||||
if (insertSuccess) {
|
||||
console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
|
||||
// console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
|
||||
} else {
|
||||
// 如果插入失败,回退到普通添加
|
||||
this.canvas.add(newImage);
|
||||
|
||||
56
src/component/Canvas/CanvasEditor/commands/PartCommands.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Command } from "./Command.js";
|
||||
|
||||
/**
|
||||
* 部件绘制命令
|
||||
*/
|
||||
export class PartDrawCommand extends Command {
|
||||
constructor(options) {
|
||||
super({
|
||||
name: "部件绘制命令",
|
||||
saveState: false,
|
||||
});
|
||||
|
||||
this.canvas = options.canvas;
|
||||
this.partManager = options.partManager;
|
||||
this.partCanvas = options.partCanvas;
|
||||
this.oldPartCanvas = this.partManager.partCanvas;
|
||||
}
|
||||
execute() {
|
||||
this.partManager.drawPartCanvas(this.partCanvas);
|
||||
return true;
|
||||
}
|
||||
undo() {
|
||||
this.partManager.drawPartCanvas(this.oldPartCanvas);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 部件点选绘制命令
|
||||
*/
|
||||
export class PartPointDrawCommand extends Command {
|
||||
constructor(options) {
|
||||
super({
|
||||
name: "部件点选绘制命令",
|
||||
saveState: false,
|
||||
});
|
||||
|
||||
this.canvas = options.canvas;
|
||||
this.partManager = options.partManager;
|
||||
this.partCanvas = options.partCanvas;
|
||||
this.pointList = options.pointList;
|
||||
this.oldPartCanvas = this.partManager.partCanvas;
|
||||
this.oldPointList = [...this.partManager.pointList];
|
||||
}
|
||||
async execute() {
|
||||
const list = [...this.pointList];
|
||||
const canvas = this.partCanvas;
|
||||
const res = await this.partManager.pointDrawPartCanvas(list, canvas);
|
||||
return res;
|
||||
}
|
||||
async undo() {
|
||||
const list = [...this.oldPointList];
|
||||
const canvas = this.oldPartCanvas;
|
||||
const res = await this.partManager.pointDrawPartCanvas(list, canvas);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -285,17 +285,15 @@ export class RasterizeLayerCommand extends Command {
|
||||
// 提取排序后的对象
|
||||
this.objectsToRasterize = objectsWithZIndex.map((item) => item.object);
|
||||
|
||||
console.log(
|
||||
`📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合`
|
||||
);
|
||||
console.log(
|
||||
"🔢 对象z-index顺序:",
|
||||
objectsWithZIndex.map((item) => ({
|
||||
id: item.object.id,
|
||||
type: item.object.type,
|
||||
zIndex: item.zIndex,
|
||||
}))
|
||||
);
|
||||
// console.log(`📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合` );
|
||||
// console.log(
|
||||
// "🔢 对象z-index顺序:",
|
||||
// objectsWithZIndex.map((item) => ({
|
||||
// id: item.object.id,
|
||||
// type: item.object.type,
|
||||
// zIndex: item.zIndex,
|
||||
// }))
|
||||
// );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -611,17 +609,17 @@ export class ExportLayerToImageCommand extends Command {
|
||||
// 提取排序后的对象
|
||||
this.objectsToRasterize = objectsWithZIndex.map((item) => item.object);
|
||||
|
||||
console.log(
|
||||
`📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合`
|
||||
);
|
||||
console.log(
|
||||
"🔢 对象z-index顺序:",
|
||||
objectsWithZIndex.map((item) => ({
|
||||
id: item.object.id,
|
||||
type: item.object.type,
|
||||
zIndex: item.zIndex,
|
||||
}))
|
||||
);
|
||||
// console.log(
|
||||
// `📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合`
|
||||
// );
|
||||
// console.log(
|
||||
// "🔢 对象z-index顺序:",
|
||||
// objectsWithZIndex.map((item) => ({
|
||||
// id: item.object.id,
|
||||
// type: item.object.type,
|
||||
// zIndex: item.zIndex,
|
||||
// }))
|
||||
// );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -159,6 +159,8 @@ export class BatchInitializeRedGreenModeCommand extends Command {
|
||||
absolutePositioned: true,
|
||||
opacity: 0.01, // 设置为几乎透明
|
||||
id: generateId("redGreenImageMask_"),
|
||||
rx: 15,
|
||||
ry: 15,
|
||||
});
|
||||
// this.canvas.add(this.redGreenImageMask);
|
||||
this.canvas.clipPath = this.redGreenImageMask;
|
||||
|
||||
@@ -25,7 +25,7 @@ export class TransformCommand extends Command {
|
||||
this.layerManager = options.layerManager;
|
||||
this.layers = options.layers || null;
|
||||
this.lastSelectLayerId = options.lastSelectLayerId || null; // 最后选择的图层ID
|
||||
|
||||
this.isCommand = options.isCommand == undefined ? true : options.isCommand
|
||||
const targetObject =
|
||||
findObjectById(this.canvas, this.objectId)?.object || null;
|
||||
|
||||
@@ -189,6 +189,11 @@ export class TransformCommand extends Command {
|
||||
object.set(key, value);
|
||||
});
|
||||
|
||||
if(this.isCommand && object.globalCompositeOperation_){
|
||||
object.globalCompositeOperation = object.globalCompositeOperation_;
|
||||
object.globalCompositeOperation_ = null;
|
||||
}
|
||||
|
||||
// 确保对象更新
|
||||
object.setCoords();
|
||||
}
|
||||
|
||||
@@ -154,6 +154,8 @@ const isVisible = computed(() => {
|
||||
OperationType.ERASER,
|
||||
OperationType.RED_BRUSH,
|
||||
OperationType.GREEN_BRUSH,
|
||||
OperationType.PART_BRUSH,
|
||||
OperationType.PART_ERASER,
|
||||
].includes(props.activeTool);
|
||||
});
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const layerManager = inject("layerManager");
|
||||
const isShowLayerPanel = inject("isShowLayerPanel", ref(false));
|
||||
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
activeTool: String,
|
||||
canvasWidth: Number,
|
||||
canvasHeight: Number,
|
||||
@@ -22,6 +23,7 @@ const props = defineProps({
|
||||
default: true, // 是否显示图层面板
|
||||
},
|
||||
isBackgroundChangeable: Boolean,
|
||||
isChangeCanvasSize: Boolean,
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
@@ -273,7 +275,7 @@ onMounted(() => {
|
||||
<template>
|
||||
<div class="canvas-header">
|
||||
<div class="canvas-header-wrapper">
|
||||
<span class="canvas-title">{{ $t('Canvas.Canvas') }}</span>
|
||||
<span class="canvas-title">{{ props.title || $t("Canvas.Canvas") }}</span>
|
||||
<!-- 默认设置 -->
|
||||
<!-- v-if="
|
||||
!activeTool ||
|
||||
@@ -284,6 +286,7 @@ onMounted(() => {
|
||||
<div class="setting-group">
|
||||
<span class="setting-label">{{ $t("Canvas.width") }}</span>
|
||||
<a-input-number
|
||||
:disabled="!isChangeCanvasSize"
|
||||
:value="canvasWidth?.toFixed(2)"
|
||||
class="setting-input"
|
||||
:min="1"
|
||||
@@ -300,6 +303,7 @@ onMounted(() => {
|
||||
<div class="setting-group">
|
||||
<span class="setting-label">{{ $t("Canvas.height") }}</span>
|
||||
<a-input-number
|
||||
:disabled="!isChangeCanvasSize"
|
||||
:value="canvasHeight?.toFixed(2)"
|
||||
class="setting-input"
|
||||
:min="1"
|
||||
|
||||
@@ -242,7 +242,7 @@ const canDeleteComputed = computed(() => {
|
||||
return parentLayer?.children?.length > 1;
|
||||
}
|
||||
// 否则直接返回根图层的可删除状态
|
||||
return props.layers.length > 3;
|
||||
return true;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -414,14 +414,14 @@ function deleteSelectedLayers() {
|
||||
}
|
||||
|
||||
// 检查删除后是否还有足够的普通图层
|
||||
const remainingNormalLayers = layers.value.filter(
|
||||
(layer) => !layer.isBackground && !layer.isFixed && !selectedLayerIds.value.includes(layer.id)
|
||||
).length;
|
||||
// const remainingNormalLayers = layers.value.filter(
|
||||
// (layer) => !layer.isBackground && !layer.isFixed && !selectedLayerIds.value.includes(layer.id)
|
||||
// ).length;
|
||||
|
||||
if (remainingNormalLayers < 1) {
|
||||
console.warn("不能删除所有普通图层");
|
||||
return;
|
||||
}
|
||||
// if (remainingNormalLayers < 1) {
|
||||
// console.warn("不能删除所有普通图层");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 确认删除
|
||||
if (selectedLayers.length > 1) {
|
||||
@@ -584,15 +584,16 @@ function handleLayerClick(layer, event) {
|
||||
// 如果不是多选模式,才可激活图层
|
||||
// 1.如果是组,则设置组下的第一个子图层为活动图层
|
||||
// 2.否则直接设置活动图层
|
||||
if (isGroupLayerType(layer) && layer.children && layer.children.length > 0) {
|
||||
if (isGroupLayerType(layer) && layer.children && layer.children.length > 0 && !layer.isPrintTrimsGroup) {
|
||||
// 如果是组图层,设置第一个子图层为活动图层
|
||||
layerManager?.setAllActiveGroupLayerCanvasObject?.(layer);
|
||||
setActiveLayer(layer.children[0].id, { parentId: layer.id });
|
||||
} else {
|
||||
let id = layer.isPrintTrimsGroup ? layer.children?.[0]?.id || layer.id : layer.id;
|
||||
// 选中画布中的图层对象
|
||||
layerManager?.selectLayerObjects(layer.id);
|
||||
layerManager?.selectLayerObjects(id);
|
||||
// 否则直接设置当前图层为活动图层
|
||||
setActiveLayer(layer.id);
|
||||
setActiveLayer(id);
|
||||
layerManager?.updateLayersObjectsInteractivity();
|
||||
}
|
||||
}
|
||||
@@ -876,13 +877,15 @@ function toggleSelectedLayersVisibility() {
|
||||
|
||||
function canDeleteLayers() {
|
||||
const selectedLayers = getSelectedLayers();
|
||||
console.log(selectedLayers);
|
||||
if (selectedLayers.length === 0) return false;
|
||||
|
||||
// 检查是否包含不能删除的图层
|
||||
const undeletableLayers = selectedLayers.filter((layer) => layer.isBackground || layer.isFixed);
|
||||
|
||||
if (undeletableLayers.length > 0) return false;
|
||||
|
||||
return true
|
||||
|
||||
// 检查删除后是否还有足够的普通图层
|
||||
const remainingNormalLayers = layers.value.filter(
|
||||
(layer) => !layer.isBackground && !layer.isFixed && !selectedLayerIds.value.includes(layer.id)
|
||||
|
||||
@@ -119,7 +119,7 @@ export default defineComponent({
|
||||
})
|
||||
const palletRef = ref(null)
|
||||
watch(()=>palletData.color_,(newVal:any)=>{
|
||||
if(!newVal?.rgba?.r)return
|
||||
if(newVal?.rgba?.r == null)return
|
||||
if(palletData.color?.gradient?.gradientShow){
|
||||
palletData.color.gradient.gradientList[palletData.color.gradient.selectIndex].rgba = {
|
||||
r:newVal.rgba.r,
|
||||
@@ -143,7 +143,7 @@ export default defineComponent({
|
||||
},{deep: true })
|
||||
const setOperate = ()=>{
|
||||
if(!palletData.color.rgba)return message.info(t('DesignDetailAlter.jsContent7'))
|
||||
palletData.color.rgba = palletData.color?.rgba?.r?palletData.color.rgba:{r:0,g:0,b:0,a:1}
|
||||
palletData.color.rgba = palletData.color?.rgba?.r != null?palletData.color.rgba:{r:0,g:0,b:0,a:1}
|
||||
palletData.gradient.selectIndex = 0
|
||||
palletData.gradient.gradientShow = true
|
||||
if(!palletData.color.gradient){
|
||||
@@ -257,7 +257,7 @@ export default defineComponent({
|
||||
|
||||
}
|
||||
const openPallet = ()=>{
|
||||
if(palletData.palletShow && props.selectColor?.rgba?.r){
|
||||
if(palletData.palletShow && props.selectColor?.rgba?.r != null){
|
||||
if(props.selectColor.gradient){
|
||||
palletData.color_.rgba = props.selectColor.gradient.gradientList[0].rgba
|
||||
}else{
|
||||
|
||||
@@ -15,6 +15,20 @@
|
||||
{{ t("Canvas.GarmentPartSelector") }}
|
||||
</div>
|
||||
<!-- 移除关闭按钮,完全通过工具切换控制显示隐藏 -->
|
||||
<div class="tip">
|
||||
<div>
|
||||
<img
|
||||
src="/src/assets/images/canvas/shubiao-l.png"
|
||||
/>
|
||||
<span>{{ t("Canvas.LeftClickAdd") }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
src="/src/assets/images/canvas/shubiao-r.png"
|
||||
/>
|
||||
<span>{{ t("Canvas.RightClickRemove") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tool-types">
|
||||
@@ -23,9 +37,9 @@
|
||||
:key="item.type"
|
||||
:class="[
|
||||
'tool-btn',
|
||||
{ active: selectionType === item.type },
|
||||
{ active: toolType === item.type },
|
||||
]"
|
||||
@click="setSelectionType(item.type)"
|
||||
@click="setPartType(item.type)"
|
||||
>
|
||||
<svg-icon :name="item.icon" :size="item.size" />
|
||||
<span>{{ item.label }}</span>
|
||||
@@ -37,23 +51,21 @@
|
||||
|
||||
<!-- 底部选区操作工具栏 -->
|
||||
<div class="tool-actions">
|
||||
<div class="action-btn" @click="copySelectionToNewLayer">
|
||||
<div class="action-btn" @click="onCreate">
|
||||
<svg-icon name="CPaste" size="16" />
|
||||
<span class="btn-text">{{
|
||||
$t("Canvas.creation")
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="action-btn" @click="cutSelectionToNewLayer">
|
||||
<!-- <div class="action-btn" @click="onCopyCreate">
|
||||
<svg-icon name="CCut" size="26" />
|
||||
<span class="btn-text">{{
|
||||
$t("Canvas.CreateAndCopy")
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="action-btn" @click="clearSelectionContent">
|
||||
<svg-icon name="CClear" size="18" />
|
||||
<span class="btn-text">{{
|
||||
$t("Canvas.TheClearlySelectedContent")
|
||||
}}</span>
|
||||
</div> -->
|
||||
<div class="action-btn" @click="onReset">
|
||||
<svg-icon name="CCut" size="26" />
|
||||
<span class="btn-text">{{ $t("Canvas.TheClearlySelectedContent") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,23 +76,9 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {
|
||||
CreateSelectionCommand,
|
||||
InvertSelectionCommand,
|
||||
FeatherSelectionCommand,
|
||||
FillSelectionCommand,
|
||||
} from "../commands/SelectionCommands";
|
||||
import { ToolCommand } from "../commands/ToolCommands";
|
||||
import {
|
||||
LassoCutoutCommand,
|
||||
ClearSelectionCommand,
|
||||
// CutSelectionToNewLayerCommand,
|
||||
} from "../commands/LassoCutoutCommand";
|
||||
|
||||
import { OperationType } from "../utils/layerHelper";
|
||||
import { ClearSelectionContentCommand } from "../commands/ClearSelectionContentCommand";
|
||||
import { CutSelectionToNewLayerCommand } from "../commands/CutSelectionToNewLayerCommand";
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
canvas: {
|
||||
type: Object,
|
||||
@@ -90,7 +88,7 @@
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
selectionManager: {
|
||||
partManager: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
@@ -115,7 +113,7 @@
|
||||
|
||||
// 响应式数据
|
||||
const visible = ref(false);
|
||||
const selectionType = ref("rectangle");
|
||||
const toolType = ref(OperationType.PART);
|
||||
//打开隐藏操作面板
|
||||
const closePanel = ref(false);
|
||||
const setClosePanel = () => {
|
||||
@@ -125,32 +123,30 @@
|
||||
const toolList = [
|
||||
{
|
||||
type: OperationType.PART,
|
||||
label: "Point Selection",
|
||||
label: t("Canvas.PointSelection"),
|
||||
icon: "CPoint",
|
||||
size: "20",
|
||||
},
|
||||
{
|
||||
type: OperationType.PART_RECTANGLE,
|
||||
label: "Marquee Selection",
|
||||
icon: "CRectangle",
|
||||
size: "26",
|
||||
label: t("Canvas.MarqueeSelection"),
|
||||
icon: "CMarquee",
|
||||
size: "20",
|
||||
},
|
||||
{
|
||||
type: OperationType.PART_BRUSH,
|
||||
label: "Brush Selection",
|
||||
icon: "CBrush",
|
||||
size: "24",
|
||||
label: t("Canvas.BrushSelection"),
|
||||
icon: "CBrush2",
|
||||
size: "16",
|
||||
},
|
||||
{
|
||||
type: OperationType.PART_ERASER,
|
||||
label: "Erase",
|
||||
icon: "CEraser",
|
||||
size: "24",
|
||||
label: t("Canvas.Erase"),
|
||||
icon: "CEraser2",
|
||||
size: "22",
|
||||
},
|
||||
];
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n();
|
||||
|
||||
onMounted(() => {});
|
||||
|
||||
@@ -169,13 +165,7 @@
|
||||
if (selectionTools.includes(newTool)) {
|
||||
show();
|
||||
// 根据工具类型设置选区类型
|
||||
selectionType.value = newTool;
|
||||
|
||||
// 更新选区管理器的选区类型
|
||||
if (props.selectionManager) {
|
||||
props.selectionManager.setSelectionType(selectionType.value);
|
||||
props.selectionManager.setupSelectionEvents();
|
||||
}
|
||||
toolType.value = newTool;
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
@@ -201,20 +191,28 @@
|
||||
/**
|
||||
* 设置选区类型
|
||||
*/
|
||||
function setSelectionType(type) {
|
||||
selectionType.value = type;
|
||||
function setPartType(type) {
|
||||
toolType.value = type;
|
||||
|
||||
// 通过 ToolManager 切换工具,这会自动通知 SelectionManager
|
||||
// 通过 ToolManager 切换工具,这会自动通知 partManager
|
||||
if (props.toolManager) {
|
||||
props.toolManager.setToolWithCommand(type);
|
||||
}
|
||||
|
||||
// 备用方案:如果没有 toolManager,直接更新 selectionManager
|
||||
else if (props.selectionManager) {
|
||||
props.selectionManager.setSelectionType(type);
|
||||
props.selectionManager.setupSelectionEvents();
|
||||
}
|
||||
}
|
||||
|
||||
// 创建
|
||||
function onCreate() {
|
||||
props.partManager.createPart();
|
||||
}
|
||||
// 复制并创建
|
||||
function onCopyCreate() {
|
||||
|
||||
}
|
||||
// 清空当前点位
|
||||
function onReset() {
|
||||
props.partManager.clearPart();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@@ -290,6 +288,28 @@
|
||||
// border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 8px 8px 0 0;
|
||||
position: relative;
|
||||
> .tip {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 10px;
|
||||
> img {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
> span {
|
||||
font-size: 10px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-title {
|
||||
@@ -370,33 +390,9 @@
|
||||
|
||||
.tool-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 5px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
/* 平板适配 - 每行4个按钮 */
|
||||
@media screen and (max-width: 768px) {
|
||||
.tool-actions {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px 6px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机适配 - 每行3个按钮 */
|
||||
@media screen and (max-width: 480px) {
|
||||
.tool-actions {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 6px 4px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.header-btn {
|
||||
font-size: 11px;
|
||||
padding: 2px 4px;
|
||||
min-width: 28px;
|
||||
}
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
@@ -436,5 +432,4 @@
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<span class="label">{{ t("Canvas.scale") }}</span>
|
||||
<slider
|
||||
:min="1"
|
||||
:max="500"
|
||||
:max="1000"
|
||||
:step="1"
|
||||
is-input
|
||||
:tipFormatter="(v) => `${scale}%`"
|
||||
@@ -32,8 +32,8 @@
|
||||
is-input
|
||||
:tipFormatter="(v) => `${v}px`"
|
||||
:value="gapX"
|
||||
@input="(e) => emit('inputFill_Gap', e, gapY)"
|
||||
@change="(e) => emit('changeFill_Gap', e, gapY)"
|
||||
@input="(e) => emit('inputFillGap', e, gapY)"
|
||||
@change="(e) => emit('changeFillGap', e, gapY)"
|
||||
/>
|
||||
</div>
|
||||
<div class="repeat-setting-item">
|
||||
@@ -45,26 +45,26 @@
|
||||
is-input
|
||||
:tipFormatter="(v) => `${v}px`"
|
||||
:value="gapY"
|
||||
@input="(e) => emit('inputFill_Gap', gapX, e)"
|
||||
@change="(e) => emit('changeFill_Gap', gapX, e)"
|
||||
@input="(e) => emit('inputFillGap', gapX, e)"
|
||||
@change="(e) => emit('changeFillGap', gapX, e)"
|
||||
/>
|
||||
</div>
|
||||
<div class="repeat-setting-item">
|
||||
<span class="label">{{ t("Canvas.offset") }}</span>
|
||||
<offset-tool
|
||||
:left="offsetX"
|
||||
:top="offsetY"
|
||||
@input="(e) => emit('inputFillOffset', e)"
|
||||
@change="(e) => emit('changeFillOffset', e)"
|
||||
:left="offset.x"
|
||||
:top="offset.y"
|
||||
@input="inputFillOffset"
|
||||
@change="changeFillOffset"
|
||||
:show-dish="false"
|
||||
/>
|
||||
</div>
|
||||
<div class="repeat-setting-item offset">
|
||||
<offset-tool
|
||||
:left="offsetX"
|
||||
:top="offsetY"
|
||||
@input="(e) => emit('inputFillOffset', e)"
|
||||
@change="(e) => emit('changeFillOffset', e)"
|
||||
:left="offset.x"
|
||||
:top="offset.y"
|
||||
@input="inputFillOffset"
|
||||
@change="changeFillOffset"
|
||||
:show-input="false"
|
||||
/>
|
||||
</div>
|
||||
@@ -79,6 +79,16 @@
|
||||
import Slider from "../tools/Slider.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits([
|
||||
"inputFillAngle",
|
||||
"changeFillAngle",
|
||||
"inputFillOffset",
|
||||
"changeFillOffset",
|
||||
"inputFillScale",
|
||||
"changeFillScale",
|
||||
"inputFillGap",
|
||||
"changeFillGap",
|
||||
]);
|
||||
|
||||
const props = defineProps({
|
||||
object: {
|
||||
@@ -89,36 +99,53 @@
|
||||
const angle = computed(
|
||||
() => getTransformScaleAngle(props.object.fill?.patternTransform).angle
|
||||
);
|
||||
const scale = computed(() => {
|
||||
const patternTransform = props.object.fill?.patternTransform;
|
||||
const scaleValue = getTransformScaleAngle(patternTransform).scale * 100;
|
||||
return Number(Number(scaleValue).toFixed(2));
|
||||
});
|
||||
const gapX = computed(() => props.object.fill_?.gapX || 0);
|
||||
const gapY = computed(() => props.object.fill_?.gapY || 0);
|
||||
const offsetX = computed(
|
||||
() => (props.object.fill?.offsetX / props.object.width) * 100
|
||||
);
|
||||
const offsetY = computed(
|
||||
() => (props.object.fill?.offsetY / props.object.height) * 100
|
||||
);
|
||||
const emit = defineEmits([
|
||||
"inputFillAngle",
|
||||
"changeFillAngle",
|
||||
"inputFillOffset",
|
||||
"changeFillOffset",
|
||||
"inputFillScale",
|
||||
"changeFillScale",
|
||||
"inputFill_Gap",
|
||||
"changeFill_Gap",
|
||||
]);
|
||||
const inputFillScale = (e) => {
|
||||
|
||||
// 缩放比例
|
||||
const scale = computed(() => {
|
||||
const object = props.object;
|
||||
const patternTransform = object.fill?.patternTransform;
|
||||
const scaleValue = getTransformScaleAngle(patternTransform).scale;
|
||||
const scaleX = scaleValue / (object.width / object.fill_.width / 5);
|
||||
const scaleY = scaleValue / (object.height / object.fill_.height / 5);
|
||||
const scaleXY = object.width > object.height ? scaleX : scaleY;
|
||||
return Number(Number(scaleXY * 100).toFixed(2));
|
||||
});
|
||||
const inputFillScale = (e) => setFillScale(e, true);
|
||||
const changeFillScale = (e) => setFillScale(e, false);
|
||||
const setFillScale = (e, isInput) => {
|
||||
const object = props.object;
|
||||
const scale = e / 100;
|
||||
emit("inputFillScale", scale);
|
||||
const scaleX = (object.width / object.fill_.width / 5) * scale;
|
||||
const scaleY = (object.height / object.fill_.height / 5) * scale;
|
||||
const scaleXY = object.width > object.height ? scaleX : scaleY;
|
||||
emit(isInput ? "inputFillScale" : "changeFillScale", scaleXY);
|
||||
};
|
||||
const changeFillScale = (e) => {
|
||||
const scale = e / 100;
|
||||
emit("changeFillScale", scale);
|
||||
|
||||
// 偏移量
|
||||
const offset = computed(() => {
|
||||
const object = props.object;
|
||||
const patternTransform = object.fill?.patternTransform;
|
||||
const scale = getTransformScaleAngle(patternTransform).scale;
|
||||
const offsetX = object.fill?.offsetX;
|
||||
const offsetY = object.fill?.offsetY;
|
||||
const twidth = object.fill_?.width;
|
||||
const theight = object.fill_?.height;
|
||||
const x = ((offsetX + (twidth * scale) / 2) * 100) / object.width;
|
||||
const y = ((offsetY + (theight * scale) / 2) * 100) / object.height;
|
||||
return { x, y };
|
||||
});
|
||||
const inputFillOffset = (e) => setFillOffset(e, true);
|
||||
const changeFillOffset = (e) => setFillOffset(e, false);
|
||||
const setFillOffset = (e, isInput) => {
|
||||
const { left, top } = e;
|
||||
const object = props.object;
|
||||
const patternTransform = object.fill?.patternTransform;
|
||||
const scale = getTransformScaleAngle(patternTransform).scale;
|
||||
const x = (left / 100) * object.width - (object.fill_?.width * scale) / 2;
|
||||
const y = (top / 100) * object.height - (object.fill_?.height * scale) / 2;
|
||||
emit(isInput ? "inputFillOffset" : "changeFillOffset", { x, y });
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -126,6 +153,7 @@
|
||||
.repeat-setting {
|
||||
user-select: none;
|
||||
width: 228px;
|
||||
overflow: hidden;
|
||||
> .title {
|
||||
line-height: 35px;
|
||||
font-size: 14px;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
v-for="v in activeObjects"
|
||||
:key="v.id"
|
||||
>
|
||||
<div class="title">{{ v.layer?.name }}</div>
|
||||
<!-- <div class="title">{{ v.layer?.name }}</div> -->
|
||||
<div class="list">
|
||||
<div
|
||||
class="input"
|
||||
@@ -125,6 +125,10 @@
|
||||
"
|
||||
:options="selectOptions"
|
||||
@change="(e) => changeFillRepeat(e, v)"
|
||||
:disabled="
|
||||
v.layer?.metadata?.sourceData?.type ===
|
||||
'trims'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<!-- 平铺设置 -->
|
||||
@@ -154,11 +158,11 @@
|
||||
@changeFillScale="
|
||||
(e) => changeFillScale(e, v)
|
||||
"
|
||||
@inputFill_Gap="
|
||||
(x, y) => inputFill_Gap(x, y, v)
|
||||
@inputFillGap="
|
||||
(x, y) => inputFillGap(x, y, v)
|
||||
"
|
||||
@changeFill_Gap="
|
||||
(x, y) => changeFill_Gap(x, y, v)
|
||||
@changeFillGap="
|
||||
(x, y) => changeFillGap(x, y, v)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
@@ -282,10 +286,10 @@
|
||||
activeObjects.value.forEach((v) => {
|
||||
v.layer = props.layerManager.getLayerById(v.layerId);
|
||||
});
|
||||
if (activeObjects.value.length === 0) {
|
||||
close();
|
||||
} else {
|
||||
if (activeObjects.value.length === 1) {
|
||||
show();
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
};
|
||||
//取消当前选中
|
||||
@@ -311,6 +315,7 @@
|
||||
layerManager: props.layerManager,
|
||||
layers: layers,
|
||||
lastSelectLayerId: lastSelectLayerId,
|
||||
isCommand,
|
||||
});
|
||||
if (isCommand) {
|
||||
props.commandManager.execute(cmd);
|
||||
@@ -332,6 +337,7 @@
|
||||
const finalState = computeAngleState(angle, obj, initialState);
|
||||
transformObject(obj, initialState, finalState, false);
|
||||
if (!obj.hasOwnProperty("oldState")) obj.oldState = initialState;
|
||||
props.canvasManager.beforeChangeCanvas([obj]);
|
||||
};
|
||||
const changeAngle = (angle, obj) => {
|
||||
var initialState;
|
||||
@@ -424,6 +430,7 @@
|
||||
});
|
||||
obj.set("fill", pattern);
|
||||
props.canvas.renderAll();
|
||||
props.canvasManager.beforeChangeCanvas([obj]);
|
||||
};
|
||||
const changeFillAngle = (angle, obj) => {
|
||||
const fill = obj.get("fill");
|
||||
@@ -433,21 +440,22 @@
|
||||
};
|
||||
changeFill(obj, pattern);
|
||||
};
|
||||
// 改变填充便宜
|
||||
// 改变填充偏移
|
||||
const inputFillOffset = (value, obj) => {
|
||||
if (!obj.oldPattern) obj.oldPattern = obj.get("fill");
|
||||
const pattern = new fabric.Pattern({
|
||||
...obj.get("fill"),
|
||||
offsetX: (value.left / 100) * obj.width,
|
||||
offsetY: (value.top / 100) * obj.height,
|
||||
offsetX: value.x,
|
||||
offsetY: value.y,
|
||||
});
|
||||
obj.set("fill", pattern);
|
||||
props.canvas.renderAll();
|
||||
props.canvasManager.beforeChangeCanvas([obj]);
|
||||
};
|
||||
const changeFillOffset = (value, obj) => {
|
||||
const pattern = new fabric.Pattern({
|
||||
offsetX: (value.left / 100) * obj.width,
|
||||
offsetY: (value.top / 100) * obj.height,
|
||||
offsetX: value.x,
|
||||
offsetY: value.y,
|
||||
});
|
||||
changeFill(obj, pattern);
|
||||
};
|
||||
@@ -462,6 +470,7 @@
|
||||
});
|
||||
obj.set("fill", pattern);
|
||||
props.canvas.renderAll();
|
||||
props.canvasManager.beforeChangeCanvas([obj]);
|
||||
};
|
||||
const changeFillScale = (scale, obj) => {
|
||||
const fill = obj.get("fill");
|
||||
@@ -483,7 +492,7 @@
|
||||
props.commandManager.execute(cmd);
|
||||
};
|
||||
// 改变填充间隙
|
||||
const inputFill_Gap = (gapX, gapY, obj) => {
|
||||
const inputFillGap = (gapX, gapY, obj) => {
|
||||
const cmd = new FillRepeatGapChangeCommand({
|
||||
canvas: props.canvas,
|
||||
layers: layers,
|
||||
@@ -494,9 +503,10 @@
|
||||
newGapY: gapY,
|
||||
record: true,
|
||||
});
|
||||
cmd.execute();
|
||||
cmd.execute(false);
|
||||
props.canvasManager.beforeChangeCanvas([obj]);
|
||||
};
|
||||
const changeFill_Gap = (gapX, gapY, obj) => {
|
||||
const changeFillGap = (gapX, gapY, obj) => {
|
||||
if (obj.oldFill_) {
|
||||
obj.fill_ = { ...obj.oldFill_ };
|
||||
delete obj.oldFill_;
|
||||
@@ -760,7 +770,7 @@
|
||||
}
|
||||
|
||||
.tool-content {
|
||||
overflow-y: auto;
|
||||
// overflow-y: auto;
|
||||
max-height: 20rem;
|
||||
margin-top: 1rem;
|
||||
padding: 0 1.5rem;
|
||||
|
||||
@@ -29,8 +29,11 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
clothingMinIOPath: {
|
||||
type: String,
|
||||
default: "", // 衣服底图URL-线稿
|
||||
},
|
||||
});
|
||||
|
||||
const commandManager = inject("commandManager");
|
||||
const layerManager = inject("layerManager"); // 图层管理器
|
||||
|
||||
@@ -249,7 +252,13 @@ const redGreenToolsList = ref([
|
||||
|
||||
// 根据模式选择工具列表
|
||||
const toolsList = computed(() => {
|
||||
return props.isRedGreenMode ? redGreenToolsList.value : normalToolsList.value;
|
||||
const list = props.isRedGreenMode ? redGreenToolsList.value : normalToolsList.value;
|
||||
return list.filter(tool => {
|
||||
if(tool.id === OperationType.PART){
|
||||
return !!props.clothingMinIOPath;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
function selectTool(tool, isRedGreenMode = false) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
isGroupLayer,
|
||||
OperationType,
|
||||
OperationTypes,
|
||||
findLayer,
|
||||
createLayer,
|
||||
LayerType,
|
||||
SpecialLayerId,
|
||||
@@ -20,7 +19,6 @@ import { AnimationManager } from "./animation/AnimationManager";
|
||||
import { createCanvas } from "../utils/canvasFactory";
|
||||
import { CanvasEventManager } from "./events/CanvasEventManager";
|
||||
import CanvasConfig from "../config/canvasConfig";
|
||||
import { RedGreenModeManager } from "./RedGreenModeManager";
|
||||
import { EraserStateManager } from "./EraserStateManager";
|
||||
import {
|
||||
deepClone,
|
||||
@@ -31,9 +29,11 @@ import {
|
||||
fillToCssStyle,
|
||||
calculateRotatedTopLeftDeg,
|
||||
calculateCenterPoint,
|
||||
calculateTopLeftPoint,
|
||||
createPatternTransform,
|
||||
getTransformScaleAngle,
|
||||
base64ToCanvas,
|
||||
imageAddGapToCanvas,
|
||||
} from "../utils/helper";
|
||||
import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands";
|
||||
import { isFunction } from "lodash-es";
|
||||
@@ -68,8 +68,11 @@ export class CanvasManager {
|
||||
this.isFixedErasable = options.isFixedErasable || false; // 是否允许擦除固定图层
|
||||
this.eraserStateManager = null; // 橡皮擦状态管理器引用
|
||||
this.handleCanvasInit = null; // 画布初始化回调函数
|
||||
this.partManager = options.partManager || null;
|
||||
this.props = options.props || {};
|
||||
this.emit = options.emit || (() => {});
|
||||
this.awaitCanvasRun = null;
|
||||
this.canvasChangeing = false;
|
||||
// 初始化画布
|
||||
this.initializeCanvas();
|
||||
}
|
||||
@@ -172,7 +175,12 @@ export class CanvasManager {
|
||||
_initCanvasEvents() {
|
||||
// 添加笔刷图像转换处理回调
|
||||
this.canvas.onBrushImageConverted = async (fabricImage) => {
|
||||
await this.addImageToLayer({ fabricImage, targetLayerId: null });
|
||||
const activeTool = this.toolManager?.activeTool?.value;
|
||||
if(activeTool === OperationType.PART_BRUSH){
|
||||
this.partManager?.addDrawPartImage(fabricImage);
|
||||
}else{
|
||||
await this.addImageToLayer({ fabricImage, targetLayerId: null });
|
||||
}
|
||||
// 返回false表示使用默认行为(直接添加到画布)
|
||||
return false;
|
||||
};
|
||||
@@ -194,6 +202,10 @@ export class CanvasManager {
|
||||
console.log("擦除完成", e.targets);
|
||||
// 可以在这里保存状态到命令管理器
|
||||
const affectedObjects = e.targets || [];
|
||||
const activeTool = this.toolManager?.activeTool?.value;
|
||||
if(activeTool === OperationType.PART_ERASER){
|
||||
return this.partManager?.onErasingEnd(affectedObjects);
|
||||
}
|
||||
const command = this.eraserStateManager.endErasing(affectedObjects);
|
||||
if (command && this.commandManager) {
|
||||
await this.commandManager?.executeCommand?.(command);
|
||||
@@ -325,6 +337,7 @@ export class CanvasManager {
|
||||
setupCanvasEvents(activeElementId, layerManager) {
|
||||
// 创建画布事件管理器
|
||||
this.eventManager = new CanvasEventManager(this.canvas, {
|
||||
canvasManager: this,
|
||||
toolManager: this.toolManager,
|
||||
animationManager: this.animationManager,
|
||||
thumbnailManager: this.thumbnailManager,
|
||||
@@ -442,12 +455,41 @@ export class CanvasManager {
|
||||
}
|
||||
|
||||
// 居中所有画布元素,包括背景层和其他元素
|
||||
this.centerAllObjects();
|
||||
await this.centerAllObjects();
|
||||
|
||||
// // 重新渲染画布使变更生效
|
||||
// this.canvas.renderAll();
|
||||
}
|
||||
|
||||
// 重置画布大小参照固定图层
|
||||
async resetCanvasSizeByFixedLayer(){
|
||||
// 重置画布大小为固定图层的大小
|
||||
const fixedLayerObj = this.getFixedLayerObject();
|
||||
const backgroundObject = this.getBackgroundLayerObject();
|
||||
if (!fixedLayerObj || !backgroundObject) return
|
||||
const fwidth = fixedLayerObj.width * fixedLayerObj.scaleX
|
||||
const fheight = fixedLayerObj.height * fixedLayerObj.scaleY
|
||||
const bwidth = backgroundObject.width * backgroundObject.scaleX
|
||||
const bheight = backgroundObject.height * backgroundObject.scaleY
|
||||
console.log(fixedLayerObj.width,
|
||||
fixedLayerObj.scaleX,
|
||||
fixedLayerObj.height,
|
||||
fixedLayerObj.scaleY,
|
||||
backgroundObject.width,
|
||||
backgroundObject.scaleX,
|
||||
backgroundObject.height,
|
||||
backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
|
||||
if(Math.abs(fwidth/bwidth - fheight/bheight) < 0.1) return;
|
||||
this.canvasWidth.value = fwidth
|
||||
this.canvasHeight.value = fheight
|
||||
backgroundObject.set({
|
||||
width: this.canvasWidth.value,
|
||||
height: this.canvasHeight.value,
|
||||
})
|
||||
this.canvas?.clipPath?.set?.({
|
||||
width: this.canvasWidth.value,
|
||||
height: this.canvasHeight.value,
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 重置视图变换,使元素回到原始位置
|
||||
* @private
|
||||
@@ -467,7 +509,6 @@ export class CanvasManager {
|
||||
*/
|
||||
async centerAllObjects() {
|
||||
if (!this.canvas) return;
|
||||
|
||||
// 获取所有可见对象(不是背景元素的对象)
|
||||
const allObjects = this.canvas.getObjects();
|
||||
if (allObjects.length === 0) return;
|
||||
@@ -482,9 +523,6 @@ export class CanvasManager {
|
||||
// 获取背景对象
|
||||
const backgroundObject = visibleObjects.find((obj) => obj.isBackground);
|
||||
|
||||
// !this.canvas?.clipPath &&
|
||||
// this.centerBackgroundLayer(this.canvas.width, this.canvas.height);
|
||||
|
||||
this.canvas?.clipPath?.set?.({
|
||||
left: this.width / 2,
|
||||
top: this.height / 2,
|
||||
@@ -576,6 +614,9 @@ export class CanvasManager {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
!this.canvas?.clipPath &&
|
||||
this.centerBackgroundLayer(this.canvas.width, this.canvas.height);
|
||||
|
||||
// 如果有背景层,更新蒙层位置
|
||||
if (backgroundObject && CanvasConfig.isCropBackground) {
|
||||
@@ -827,9 +868,9 @@ export class CanvasManager {
|
||||
|
||||
return layerObjectByLayerId;
|
||||
}
|
||||
getObjectsByIds(ids){
|
||||
getObjectsByIdOrLayerId(ids){
|
||||
const objects = this.canvas.getObjects().filter((obj) => {
|
||||
return ids.includes(obj.id);
|
||||
return ids.includes(obj.id) || ids.includes(obj.layerId);
|
||||
});
|
||||
return objects;
|
||||
}
|
||||
@@ -841,7 +882,6 @@ export class CanvasManager {
|
||||
updateMaskPosition(backgroundLayerObject) {
|
||||
if (!backgroundLayerObject || !this.maskLayer || !this.canvas.clipPath)
|
||||
return;
|
||||
console.log("backgroundLayerObject");
|
||||
const left = backgroundLayerObject.left;
|
||||
const top = backgroundLayerObject.top;
|
||||
|
||||
@@ -938,6 +978,7 @@ export class CanvasManager {
|
||||
* @param {Boolean} options.isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} options.isPrintTrimsNoRepeat 是否包含印花图层的不平铺
|
||||
* @param {Boolean} options.isPrintTrimsRepeat 是否包含印花图层的平铺
|
||||
* @param {Boolean} options.isContainNormalLayer 是否包含普通图层
|
||||
* @param {String} options.layerId 导出具体图层ID
|
||||
* @param {Array} options.layerIdArray 导出多个图层ID数组
|
||||
* @param {String} options.expPicType 导出图片类型 (png/jpg/svg)
|
||||
@@ -960,6 +1001,7 @@ export class CanvasManager {
|
||||
const enhancedOptions = {
|
||||
isPrintTrimsNoRepeat: true,
|
||||
isPrintTrimsRepeat: true,
|
||||
isContainNormalLayer: true,
|
||||
...options,
|
||||
// 如果没有明确指定,则根据当前模式自动设置
|
||||
restoreOpacityInRedGreen:
|
||||
@@ -1046,10 +1088,12 @@ export class CanvasManager {
|
||||
// 导出印花和元素图层信息
|
||||
const printTrimsData = await this.exportPrintTrimsLayers().catch(() => ({prints: null, trims: null}));
|
||||
|
||||
return {
|
||||
const obj = {
|
||||
color,
|
||||
...printTrimsData,
|
||||
};
|
||||
console.log("==========exportExtraInfo:", obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1103,7 +1147,7 @@ export class CanvasManager {
|
||||
const glayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
|
||||
if(!glayer) return Promise.reject("印花和元素图层组不存在");
|
||||
const ids = glayer.children.map((v) => v.id);
|
||||
const objects = this.getObjectsByIds(ids);
|
||||
const objects = this.getObjectsByIdOrLayerId(ids);
|
||||
const fixedLayerObj = this.getFixedLayerObject();
|
||||
if(!fixedLayerObj) return Promise.reject("固定图层不存在");
|
||||
const flWidth = fixedLayerObj.width
|
||||
@@ -1114,8 +1158,9 @@ export class CanvasManager {
|
||||
const flScaleY = fixedLayerObj.scaleY
|
||||
const prints = [];
|
||||
const trims = [];
|
||||
objects.forEach((v) => {
|
||||
const sourceData = glayer.children.find((v_) => v_.id === v.id)?.metadata?.sourceData;
|
||||
objects.forEach((v, i) => {
|
||||
const label = glayer.children.find((v_) => (v_.id === v.layerId || v_.id === v.id));
|
||||
const sourceData = label?.metadata?.sourceData;
|
||||
if(!sourceData) return;
|
||||
const obj = {
|
||||
ifSingle: typeof v.fill === "string",
|
||||
@@ -1127,7 +1172,7 @@ export class CanvasManager {
|
||||
scale: [0, 0],
|
||||
angle: v.angle,
|
||||
name: sourceData.name,
|
||||
priority: sourceData.priority,
|
||||
priority: i + 1,
|
||||
object:{
|
||||
top: 0,
|
||||
left: 0,
|
||||
@@ -1135,29 +1180,36 @@ export class CanvasManager {
|
||||
scaleY: 0,//对象的缩放比例
|
||||
opacity: v.opacity,
|
||||
angle: v.angle,
|
||||
flipX: v.flipX,//是否水平翻转
|
||||
flipY: v.flipY,//是否垂直翻转
|
||||
blendMode: v.globalCompositeOperation,// 混合模式
|
||||
flipX: v.flipX,
|
||||
flipY: v.flipY,
|
||||
blendMode: v.globalCompositeOperation,
|
||||
gapX: 0,// 平铺模式下的间距
|
||||
gapY: 0,// 平铺模式下的间距
|
||||
fill_repeat: "",
|
||||
}
|
||||
}
|
||||
let left = (v.left - (flLeft - flWidth * flScaleX / 2));
|
||||
let top = (v.top - (flTop - flHeight * flScaleY / 2));
|
||||
let width = (v.width * v.scaleX);
|
||||
let height = (v.height * v.scaleY);
|
||||
let {x:cx, y:cy} = calculateCenterPoint(width, height, left, top, v.angle);
|
||||
let oX = (cx-width/2) / flScaleX;
|
||||
let oY = (cy-height/2) / flScaleY;
|
||||
if(v.originX === "center" && v.originY === "center") {
|
||||
let {x:cx, y:cy} = calculateTopLeftPoint(width, height, left, top, v.angle);
|
||||
left = cx;
|
||||
top = cy;
|
||||
}
|
||||
let oX = left / flScaleX;
|
||||
let oY = top / flScaleY;
|
||||
let oScaleX = (v.width * v.scaleX) / (flWidth * flScaleX);
|
||||
let oScaleY = (v.height * v.scaleY) / (flHeight * flScaleY);
|
||||
// obj.object.width = width;
|
||||
// obj.object.height = height;
|
||||
obj.object.top = oY;
|
||||
obj.object.left = oX;
|
||||
obj.object.scaleX = oScaleX;
|
||||
obj.object.scaleY = oScaleY;
|
||||
if(obj.ifSingle){
|
||||
// 单个的是从中心计算的
|
||||
let {x:cx, y:cy} = calculateCenterPoint(width, height, left, top, v.angle);
|
||||
let oX = (cx-width/2) / flScaleX;
|
||||
let oY = (cy-height/2) / flScaleY;
|
||||
obj.location = [oX, oY];
|
||||
obj.scale = [oScaleX, oScaleY];
|
||||
}else{
|
||||
@@ -1175,17 +1227,19 @@ export class CanvasManager {
|
||||
obj.scale = [scaleXY, scaleXY];
|
||||
obj.angle = angle;
|
||||
obj.location = [left, top];
|
||||
obj.gap = [fill_.gapX, fill_.gapY];
|
||||
obj.object.gapX = fill_.gapX;
|
||||
obj.object.gapY = fill_.gapY;
|
||||
obj.object.fill_repeat = fill.repeat;
|
||||
}
|
||||
if(obj.level2Type === "Pattern"){
|
||||
if(sourceData.type === "print"){
|
||||
prints.push(obj);
|
||||
}else if(obj.level2Type === "Embroidery"){
|
||||
}else if(sourceData.type === "trims"){
|
||||
trims.push(obj);
|
||||
}
|
||||
})
|
||||
// prints.sort((a, b) => a.ifSingle ? 1 : -1);
|
||||
prints.forEach((v, i) => v.priority = i + 1);
|
||||
trims.forEach((v, i) => v.priority = i + 1);
|
||||
// prints.forEach((v, i) => v.priority = i + 1);
|
||||
// trims.forEach((v, i) => v.priority = i + 1);
|
||||
return {prints, trims};
|
||||
}
|
||||
|
||||
@@ -1272,6 +1326,7 @@ export class CanvasManager {
|
||||
canvasColor: this.canvasColor.value,
|
||||
activeLayerId: this.layerManager?.activeLayerId?.value,
|
||||
};
|
||||
this.FixJsonIdLoss(data);
|
||||
console.log("获取画布JSON数据...", data);
|
||||
return JSON.stringify(data);
|
||||
} catch (error) {
|
||||
@@ -1279,10 +1334,8 @@ export class CanvasManager {
|
||||
throw new Error("获取画布JSON失败");
|
||||
}
|
||||
}
|
||||
|
||||
loadJSON(json, calllBack) {
|
||||
console.log("加载画布JSON数据:", json);
|
||||
|
||||
this.canvas.loading.value = true;
|
||||
// 确保传入的json是字符串格式
|
||||
if (typeof json === "object") {
|
||||
json = JSON.stringify(json);
|
||||
@@ -1291,11 +1344,13 @@ export class CanvasManager {
|
||||
}
|
||||
// 解析JSON字符串
|
||||
try {
|
||||
const parsedJson = JSON.parse(json);
|
||||
const parsedJson = window.testCanvasJson || JSON.parse(json);
|
||||
console.log("加载画布JSON数据:", parsedJson);
|
||||
this.FixJsonIdLoss(parsedJson);
|
||||
this.canvasWidth.value = parsedJson.canvasWidth || this.width;
|
||||
this.canvasHeight.value = parsedJson.canvasHeight || this.height;
|
||||
this.canvasColor.value = parsedJson.canvasColor || this.backgroundColor;
|
||||
|
||||
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const tempLayers = parsedJson?.layers || [];
|
||||
@@ -1317,7 +1372,7 @@ export class CanvasManager {
|
||||
// this.canvasHeight.value = parsedJson.canvasHeight || this.height;
|
||||
// this.canvasColor.value = parsedJson.canvasColor || this.backgroundColor;
|
||||
|
||||
console.log("是否检测到红绿图模式内容:", this.enabledRedGreenMode);
|
||||
// console.log("是否检测到红绿图模式内容:", this.enabledRedGreenMode);
|
||||
|
||||
// 重置视图变换以确保元素位置正确
|
||||
this._resetViewportTransform(1);
|
||||
@@ -1329,7 +1384,7 @@ export class CanvasManager {
|
||||
|
||||
// 清除当前画布内容
|
||||
// this.canvas.clear(); // 清除画布内容 可以先去掉 这样加载闪动的情况就比较少 如果有问题 可以再打开
|
||||
console.log("清除当前画布内容", canvasData);
|
||||
// console.log("清除当前画布内容", canvasData);
|
||||
delete canvasData.clipPath; // 删除当前裁剪路径
|
||||
// 加载画布数据
|
||||
this.canvas.loadFromJSON(canvasData, async () => {
|
||||
@@ -1358,7 +1413,7 @@ export class CanvasManager {
|
||||
// 重置画布数据
|
||||
await this.setCanvasSize(this.canvas.width, this.canvas.height);
|
||||
await this.centerBackgroundLayer(this.canvas.width, this.canvas.height);
|
||||
await this.createOtherLayers(this.props.otherData);
|
||||
await this.resetCanvasSizeByFixedLayer();
|
||||
// 重新构建对象关系
|
||||
// restoreObjectLayerAssociations(this.layers.value, this.canvas.getObjects());
|
||||
// 验证图层关联关系 - 稳定后可以注释
|
||||
@@ -1370,7 +1425,7 @@ export class CanvasManager {
|
||||
// console.log("图层关联验证结果:", isValidate);
|
||||
// 排序
|
||||
// 使用LayerSort工具重新排列画布对象(如果可用)
|
||||
await this?.layerManager?.layerSort?.rearrangeObjects();
|
||||
await this?.layerManager?.layerSort?.rearrangeObjectsAsync();
|
||||
|
||||
this.layerManager.activeLayerId.value = this.layers.value[0]
|
||||
.children?.length
|
||||
@@ -1388,7 +1443,6 @@ export class CanvasManager {
|
||||
await this.layerManager?.updateLayersObjectsInteractivity?.();
|
||||
|
||||
await calllBack?.();
|
||||
this.emit("canvas-load-json-success");
|
||||
// 更新所有缩略图
|
||||
setTimeout(() => {
|
||||
this.updateAllThumbnails();
|
||||
@@ -1410,27 +1464,56 @@ export class CanvasManager {
|
||||
throw new Error("解析JSON失败,请检查输入格式: " + error.message);
|
||||
}
|
||||
}
|
||||
/** 修复JSON数据中的ID丢失问题 */
|
||||
FixJsonIdLoss(json){
|
||||
const layerIds = [];
|
||||
const layers = json?.layers || [];
|
||||
const objects = json?.canvas?.objects || [];
|
||||
layers.forEach((layer) => {
|
||||
layerIds.push(layer.id);
|
||||
layer.children?.forEach((child) => layerIds.push(child.id));
|
||||
if(!layer.fabricObjects?.[0]?.id && !layer.fabricObject?.id){
|
||||
const obj = objects?.find((o) => o.layerId === layer.id);
|
||||
if(obj) {
|
||||
layer.fabricObjects = [{
|
||||
id: obj.id,
|
||||
type: obj.type,
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
// 排序
|
||||
objects.sort((a, b) => {
|
||||
if (a.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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建其他图层:印花、颜色、元素...
|
||||
* @param {Object} otherData - 其他图层数据
|
||||
*/
|
||||
async createOtherLayers(otherData, isUpdate = false) {
|
||||
async createOtherLayers(otherData) {
|
||||
if (!otherData) return console.warn("otherData 为空不需要添加");
|
||||
let resolve = ()=>{};
|
||||
this.awaitCanvasRun = ()=>(new Promise((v) => resolve = v))
|
||||
const otherData_ = JSON.parse(JSON.stringify(otherData));
|
||||
console.log("==========创建其他图层", otherData_);
|
||||
|
||||
const updateColor = !!otherData_.color;
|
||||
const updateSpecialGroup = !!otherData_.printObject || !!otherData_.trims;
|
||||
// 删除颜色图层和特殊组图层
|
||||
const ids = [];
|
||||
if(isUpdate){
|
||||
updateColor && ids.push(SpecialLayerId.COLOR)
|
||||
updateSpecialGroup && ids.push(SpecialLayerId.SPECIAL_GROUP)
|
||||
}else{
|
||||
ids.push(SpecialLayerId.COLOR)
|
||||
ids.push(SpecialLayerId.SPECIAL_GROUP)
|
||||
}
|
||||
const ids = [SpecialLayerId.COLOR, SpecialLayerId.SPECIAL_GROUP];
|
||||
this.layers.value = this.layers.value.filter((layer) => {
|
||||
if(ids.includes(layer.id)){
|
||||
ids.push(...layer.children?.map((child) => child.id));
|
||||
@@ -1438,30 +1521,39 @@ export class CanvasManager {
|
||||
}
|
||||
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 singleLayers = [];// 平铺图层
|
||||
otherData_.printObject?.prints?.forEach((print, index) => {
|
||||
otherData_.printObject?.prints?.forEach((print, index) => {// 印花
|
||||
print.name = t("Canvas.Print") + (index + 1);
|
||||
print.type = "print";
|
||||
if(print.ifSingle){
|
||||
printTrimsLayers.unshift({...print});
|
||||
}else{
|
||||
singleLayers.unshift({...print});
|
||||
}
|
||||
})
|
||||
otherData_.trims?.prints?.forEach((trims, index) => {
|
||||
otherData_.trims?.prints?.forEach((trims, index) => {// 元素
|
||||
trims.name = t("Canvas.Elements") + (index + 1);
|
||||
trims.type = "trims";
|
||||
printTrimsLayers.unshift({...trims});
|
||||
})
|
||||
if(isUpdate ? updateSpecialGroup : true){
|
||||
if(printTrimsLayers.length || singleLayers.length){
|
||||
await this.createPrintTrimsLayers(printTrimsLayers, singleLayers);
|
||||
}
|
||||
await this.changeCanvas();
|
||||
console.log("==========创建其他图层成功");
|
||||
resolve();
|
||||
this.awaitCanvasRun = null;
|
||||
}
|
||||
|
||||
// 设置画布对象的裁剪信息
|
||||
@@ -1565,49 +1657,65 @@ export class CanvasManager {
|
||||
let name = item.name;
|
||||
let image = await new Promise(resolve => {
|
||||
fabric.Image.fromURL(item.path, (fabricImage)=>{
|
||||
const left = flLeft - flWidth * flScaleX / 2 + (item.location?.[0] || 0) * flScaleX
|
||||
const top = flTop - flHeight * flScaleY / 2 + (item.location?.[1] || 0) * flScaleY
|
||||
const scaleX = flWidth * (item.scale?.[0] || 1) / fabricImage.width * flScaleX
|
||||
const scaleY = flHeight * (item.scale?.[1] || 1) / fabricImage.height * flScaleY
|
||||
const {x, y} = calculateRotatedTopLeftDeg(
|
||||
fabricImage.width * scaleX,
|
||||
fabricImage.height * scaleY,
|
||||
left,
|
||||
top,
|
||||
0,
|
||||
item.angle || 0
|
||||
)
|
||||
const angle = item.angle || 0
|
||||
fabricImage.set({
|
||||
left: x,
|
||||
top: y,
|
||||
scaleX: scaleX,
|
||||
scaleY: scaleY,
|
||||
angle: angle,
|
||||
id: id,
|
||||
layerId: id,
|
||||
layerName: name,
|
||||
selectable: true,
|
||||
hasControls: true,
|
||||
hasBorders: true,
|
||||
isPrintTrims: true,
|
||||
globalCompositeOperation: BlendMode.MULTIPLY,
|
||||
});
|
||||
resolve(fabricImage);
|
||||
}, { crossOrigin: "anonymous" });
|
||||
})
|
||||
this.canvas.add(image);
|
||||
let left = flLeft - flWidth * flScaleX / 2 + (item.location?.[0] || 0) * flScaleX
|
||||
let top = flTop - flHeight * flScaleY / 2 + (item.location?.[1] || 0) * flScaleY
|
||||
let scaleX = flWidth * (item.scale?.[0] || 1) / image.width * flScaleX
|
||||
let scaleY = flHeight * (item.scale?.[1] || 1) / image.height * flScaleY
|
||||
let {x, y} = calculateRotatedTopLeftDeg(
|
||||
image.width * scaleX,
|
||||
image.height * scaleY,
|
||||
left,
|
||||
top,
|
||||
0,
|
||||
item.angle || 0
|
||||
)
|
||||
let angle = item.angle || 0
|
||||
|
||||
let opacity = 1
|
||||
let flipX = false;
|
||||
let flipY = false;
|
||||
let blendMode = BlendMode.NORMAL;
|
||||
// if(item.type === "trims") blendMode = BlendMode.NORMAL;// 元素正常
|
||||
if(item.object){
|
||||
opacity = item.object.opacity
|
||||
flipX = item.object.flipX
|
||||
flipY = item.object.flipY
|
||||
if(item.object.blendMode) blendMode = item.object.blendMode;
|
||||
}
|
||||
image.set({
|
||||
left: x,
|
||||
top: y,
|
||||
scaleX: scaleX,
|
||||
scaleY: scaleY,
|
||||
angle: angle,
|
||||
opacity: opacity,
|
||||
flipX: flipX,
|
||||
flipY: flipY,
|
||||
globalCompositeOperation: blendMode,
|
||||
id: id,
|
||||
layerId: id,
|
||||
layerName: name,
|
||||
selectable: true,
|
||||
hasControls: true,
|
||||
hasBorders: true,
|
||||
isPrintTrims: true,
|
||||
});
|
||||
// this.canvas.add(image);
|
||||
let layer = createLayer({
|
||||
id: id,
|
||||
name: name,
|
||||
type: LayerType.BITMAP,
|
||||
visible: true,
|
||||
locked: false,
|
||||
opacity: 1.0,
|
||||
opacity: opacity,
|
||||
isPrintTrims: true,
|
||||
blendMode: BlendMode.MULTIPLY,
|
||||
blendMode: blendMode,
|
||||
fabricObjects: [image.toObject(["id", "layerId", "layerName"])],
|
||||
metadata: {sourceData: item},
|
||||
object: image,
|
||||
})
|
||||
children.push(layer);
|
||||
};
|
||||
@@ -1628,50 +1736,83 @@ export class CanvasManager {
|
||||
resolve(tcanvas);
|
||||
}, { crossOrigin: "anonymous" });
|
||||
})
|
||||
let scaleX = fixedLayerObj.width / image.width * (item.scale?.[0] || 1) / 5;
|
||||
let scaleY = fixedLayerObj.height / image.height * (item.scale?.[1] || 1) / 5;
|
||||
let scale = fixedLayerObj.width > fixedLayerObj.height ? scaleX : scaleY;
|
||||
let left = (item.location?.[0] || 0) - image.width * scale / 2
|
||||
let top = (item.location?.[1] || 0) - image.height * scale / 2
|
||||
let scaleX_ = flWidth / image.width * (item.scale?.[0] || 1) / 5;
|
||||
let scaleY_ = flHeight / image.height * (item.scale?.[1] || 1) / 5;
|
||||
let scale = flWidth > flHeight ? scaleX_ : scaleY_;
|
||||
let offsetX = (item.location?.[0] || 0) - image.width * scale / 2
|
||||
let offsetY = (item.location?.[1] || 0) - image.height * scale / 2
|
||||
let top = flTop - flHeight * flScaleY / 2
|
||||
let left = flLeft - flWidth * flScaleX / 2
|
||||
let scaleX = flScaleX
|
||||
let scaleY = flScaleY
|
||||
let opacity = 1
|
||||
let angle = 0
|
||||
let gapX = 0
|
||||
let gapY = 0
|
||||
let fillSource = image
|
||||
let flipX = false;
|
||||
let flipY = false;
|
||||
let blendMode = BlendMode.NORMAL;
|
||||
let fill_repeat = "repeat"
|
||||
if(item.object){
|
||||
top += item.object.top * flScaleY
|
||||
left += item.object.left * flScaleX
|
||||
scaleX *= item.object.scaleX
|
||||
scaleY *= item.object.scaleY
|
||||
opacity = item.object.opacity
|
||||
angle = item.object.angle
|
||||
flipX = item.object.flipX
|
||||
flipY = item.object.flipY
|
||||
if(item.object.blendMode) blendMode = item.object.blendMode;
|
||||
gapX = item.object.gapX
|
||||
gapY = item.object.gapY
|
||||
fillSource = imageAddGapToCanvas(image, gapX, gapY);
|
||||
if(item.object.fill_repeat) fill_repeat = item.object.fill_repeat;
|
||||
}
|
||||
let rect = new fabric.Rect({
|
||||
id: id,
|
||||
layerId: id,
|
||||
layerName: name,
|
||||
width: fixedLayerObj.width,
|
||||
height: fixedLayerObj.height,
|
||||
top: fixedLayerObj.top - fixedLayerObj.height * fixedLayerObj.scaleY / 2,
|
||||
left: fixedLayerObj.left - fixedLayerObj.width * fixedLayerObj.scaleX / 2,
|
||||
scaleX: fixedLayerObj.scaleX,
|
||||
scaleY: fixedLayerObj.scaleY,
|
||||
globalCompositeOperation: BlendMode.MULTIPLY,
|
||||
width: flWidth,
|
||||
height: flHeight,
|
||||
top: top,
|
||||
left: left,
|
||||
scaleX: scaleX,
|
||||
scaleY: scaleY,
|
||||
opacity: opacity,
|
||||
angle: angle,
|
||||
flipX: flipX,
|
||||
flipY: flipY,
|
||||
globalCompositeOperation: blendMode,
|
||||
fill: new fabric.Pattern({
|
||||
source: image,
|
||||
repeat: "repeat",
|
||||
source: fillSource,
|
||||
repeat: fill_repeat,
|
||||
patternTransform: createPatternTransform(scale, item.angle || 0),
|
||||
offsetX: left, // 水平偏移
|
||||
offsetY: top, // 垂直偏移
|
||||
offsetX: offsetX, // 水平偏移
|
||||
offsetY: offsetY, // 垂直偏移
|
||||
}),
|
||||
fill_ : {
|
||||
source: item.path,
|
||||
gapX: 0,
|
||||
gapY: 0,
|
||||
gapX: gapX,
|
||||
gapY: gapY,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
},
|
||||
isPrintTrims: true,
|
||||
});
|
||||
this.canvas.add(rect);
|
||||
// this.canvas.add(rect);
|
||||
let layer = createLayer({
|
||||
id: id,
|
||||
name: name,
|
||||
type: LayerType.BITMAP,
|
||||
visible: true,
|
||||
locked: true,
|
||||
opacity: 1,
|
||||
locked: false,
|
||||
opacity: opacity,
|
||||
isPrintTrims: true,
|
||||
blendMode: BlendMode.MULTIPLY,
|
||||
blendMode: blendMode,
|
||||
fabricObjects: [rect.toObject(["id", "layerId", "layerName"])],
|
||||
metadata: {sourceData: item},
|
||||
object: rect,
|
||||
})
|
||||
children.push(layer);
|
||||
};
|
||||
@@ -1688,6 +1829,13 @@ export class CanvasManager {
|
||||
// children.push(layer);
|
||||
// }
|
||||
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({});
|
||||
await this.setObjecCliptInfo(groupRect);
|
||||
// 插入组图层
|
||||
@@ -1710,12 +1858,13 @@ export class CanvasManager {
|
||||
/**
|
||||
* 画布事件变更后
|
||||
*/
|
||||
async changeCanvas(){
|
||||
async changeCanvas(fids = [], isBeforeChange = false){
|
||||
if(!isBeforeChange) this.canvasChangeing = false;
|
||||
const fixedLayerObj = this.getFixedLayerObject();
|
||||
if(!fixedLayerObj) return console.warn("固定图层对象不存在", fixedLayerObj)
|
||||
const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
|
||||
if(colorObject){
|
||||
const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP);
|
||||
const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP).filter(id => !fids.includes(id));
|
||||
if(ids.length === 0){
|
||||
ids.unshift(SpecialLayerId.SPECIAL_GROUP);
|
||||
await this.setObjecCliptInfo(colorObject);
|
||||
@@ -1735,6 +1884,24 @@ export class CanvasManager {
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
}
|
||||
/** 画布变更之前 */
|
||||
async beforeChangeCanvas(objects){
|
||||
if(this.canvasChangeing) return;
|
||||
const ids = objects.filter(v => {
|
||||
return v.isPrintTrims && v.globalCompositeOperation && v.globalCompositeOperation !== BlendMode.NORMAL
|
||||
}).map(v => v.layerId);
|
||||
if(ids.length > 0){
|
||||
this.canvasChangeing = true;
|
||||
this.canvas.getObjects().forEach(v => {
|
||||
if(ids.includes(v.layerId)){
|
||||
v.globalCompositeOperation_ = v.globalCompositeOperation;
|
||||
v.globalCompositeOperation = BlendMode.NORMAL;
|
||||
}
|
||||
})
|
||||
this.canvas.renderAll();
|
||||
await this.changeCanvas(ids, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放红绿图模式内容以适应当前画布大小
|
||||
|
||||
@@ -20,6 +20,7 @@ export class ExportManager {
|
||||
* @param {Boolean} options.isContainBg 是否包含背景图层
|
||||
* @param {Boolean} options.isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} options.isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} options.isContainNormalLayer 是否包含普通图层
|
||||
* @param {Boolean} options.isCropByBg 是否使用背景大小裁剪
|
||||
* @param {String} options.layerId 导出具体图层ID
|
||||
* @param {Array} options.layerIdArray 导出多个图层ID数组
|
||||
@@ -35,6 +36,7 @@ export class ExportManager {
|
||||
isContainBg = false,
|
||||
isContainFixed = false,
|
||||
isContainFixedOther = false, // 是否包含其他固定图层
|
||||
isContainNormalLayer = true, // 是否包含普通图层
|
||||
isCropByBg = false, // 是否使用背景大小裁剪
|
||||
layerId = "",
|
||||
layerIdArray = [],
|
||||
@@ -68,6 +70,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer, // 是否包含普通图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg,
|
||||
@@ -81,6 +84,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer, // 是否包含普通图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg,
|
||||
@@ -160,6 +164,7 @@ export class ExportManager {
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||
* @param {Boolean} isRedGreenMode 是否为红绿图模式
|
||||
* @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
|
||||
* @param {Boolean} isCropByBg 是否使用背景大小裁剪
|
||||
@@ -173,6 +178,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer = true, // 是否包含普通图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg, // 是否使用背景大小裁剪
|
||||
@@ -188,6 +194,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer, // 是否包含普通图层
|
||||
);
|
||||
|
||||
if (objectsToExport.length === 0) {
|
||||
@@ -220,6 +227,7 @@ export class ExportManager {
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||
* @param {Boolean} isRedGreenMode 是否为红绿图模式
|
||||
* @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
|
||||
* @param {Boolean} isCropByBg 是否使用背景大小裁剪
|
||||
@@ -233,6 +241,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer, // 是否包含普通图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg, // 是否使用背景大小裁剪
|
||||
@@ -246,6 +255,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer, // 是否包含普通图层
|
||||
excludedLayers,
|
||||
);
|
||||
|
||||
@@ -405,11 +415,12 @@ export class ExportManager {
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||
* @param {Array} excludedLayers 排除的图层ID数组
|
||||
* @returns {Array} 按正确顺序排列的真实对象数组
|
||||
* @private
|
||||
*/
|
||||
_collectObjectsByLayerOrder(layerIdArray, isContainBg, isContainFixed, isContainFixedOther, excludedLayers) {
|
||||
_collectObjectsByLayerOrder(layerIdArray, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer, excludedLayers) {
|
||||
const objectsToExport = [];
|
||||
const allLayers = this._getAllLayersFlattened(excludedLayers); // 获取扁平化的图层列表
|
||||
|
||||
@@ -421,7 +432,7 @@ export class ExportManager {
|
||||
if (layerIdArray && !layerIdArray.includes(layer.id)) continue;
|
||||
|
||||
// 检查图层类型过滤条件
|
||||
if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther))
|
||||
if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer))
|
||||
continue;
|
||||
|
||||
if (layer.visible) {
|
||||
@@ -670,7 +681,7 @@ export class ExportManager {
|
||||
isEnhanceImg, // 是否是增强图片
|
||||
});
|
||||
|
||||
console.log("导出图片数据URL:", dataURL);
|
||||
// console.log("导出图片数据URL:", dataURL);
|
||||
return dataURL;
|
||||
|
||||
// // 创建与画布相同尺寸的临时画布
|
||||
@@ -1040,10 +1051,11 @@ export class ExportManager {
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||
* @returns {Boolean} 是否应该包含
|
||||
* @private
|
||||
*/
|
||||
_shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther) {
|
||||
_shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer) {
|
||||
if (!layer) return false;
|
||||
|
||||
// 检查背景图层
|
||||
@@ -1061,7 +1073,12 @@ export class ExportManager {
|
||||
return isContainFixedOther;
|
||||
}
|
||||
|
||||
// 普通图层总是包含
|
||||
return true;
|
||||
// 印花图层始终导出
|
||||
if (layer.isPrintTrims || layer.isPrintTrimsGroup) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 普通图层
|
||||
return isContainNormalLayer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ export class LayerManager {
|
||||
*/
|
||||
initCommandManager() {
|
||||
// 命令注册逻辑已移除,现在直接使用命令类实例化和执行
|
||||
console.log("CommandManager 已初始化,使用直接命令调用模式");
|
||||
// console.log("CommandManager 已初始化,使用直接命令调用模式");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +161,7 @@ export class LayerManager {
|
||||
this.layerSort = createLayerSort(this.canvas, this.layers, {
|
||||
commandManager: this.commandManager,
|
||||
});
|
||||
console.log("图层排序工具已初始化");
|
||||
// console.log("图层排序工具已初始化");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ export class LayerManager {
|
||||
// 更新所有对象的交互性
|
||||
this.updateLayersObjectsInteractivity();
|
||||
|
||||
console.log(`已切换到${mode}模式`);
|
||||
// console.log(`已切换到${mode}模式`);
|
||||
}
|
||||
|
||||
setToolManager(toolManager) {
|
||||
@@ -332,7 +332,7 @@ export class LayerManager {
|
||||
|
||||
// 私有方法:应用交互规则
|
||||
async _applyInteractionRules({ isMoveing }) {
|
||||
console.log("updateLayersObjectsInteractivity ===>", this.editorMode);
|
||||
// console.log("updateLayersObjectsInteractivity ===>", this.editorMode);
|
||||
const objects = this.canvas.getObjects();
|
||||
const editorMode = this.editorMode || CanvasConfig.defaultTool;
|
||||
const layers = this.layers?.value || [];
|
||||
@@ -697,6 +697,7 @@ export class LayerManager {
|
||||
* 初始化图层,确保有背景层、固定图层和一个空白图层
|
||||
*/
|
||||
async initializeLayers() {
|
||||
console.log("初始化图层",this.layers.value.length)
|
||||
// 如果没有任何图层,创建背景层、固定图层和一个空白图层
|
||||
if (this.layers.value.length === 0) {
|
||||
// 创建背景图层
|
||||
@@ -975,7 +976,7 @@ export class LayerManager {
|
||||
return !layer.isFixed && !layer.isFixedOther && !layer.isBackground
|
||||
})
|
||||
// const normalLayers = this.layers.value.filter((l) => !l.isBackground && !l.isFixed && !l.isFixedOther);
|
||||
console.log("普通图层:", normalLayers)
|
||||
// console.log("普通图层:", normalLayers)
|
||||
if (isChild ? parentLength <= 1 : false) {//normalLayers.length <= 1
|
||||
console.warn("不能删除唯一的普通图层");
|
||||
message.warning(this.t("Canvas.cannotDeleteOnlyLayer"));
|
||||
@@ -1096,14 +1097,14 @@ export class LayerManager {
|
||||
if (this.commandManager) {
|
||||
const result = this.commandManager.execute(command);
|
||||
if (result) {
|
||||
console.log(`✅ 图层移动成功: ${direction}`);
|
||||
// console.log(`✅ 图层移动成功: ${direction}`);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
const result = command.execute();
|
||||
if (result) {
|
||||
// 更新画布渲染顺序
|
||||
console.log(`✅ 图层移动成功: ${direction}`);
|
||||
// console.log(`✅ 图层移动成功: ${direction}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1147,7 +1148,6 @@ export class LayerManager {
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
console.log("==========", allObjects)
|
||||
// if (layer.fill) {
|
||||
// // 如果图层有填充颜色,设置所有对象的填充颜色
|
||||
// const { object } = findObjectById(this.canvas, layer.fill.id);
|
||||
@@ -1580,7 +1580,7 @@ export class LayerManager {
|
||||
/**
|
||||
* 排序图层,确保图层顺序: 普通图层 > 固定图层 > 背景图层
|
||||
*/
|
||||
sortLayers() {
|
||||
async sortLayers() {
|
||||
// 对图层进行排序:背景图层在最底层(数组最后),固定图层在中间
|
||||
this.layers.value.sort((a, b) => {
|
||||
// 如果a是背景图层,它应该排在后面(最底层)
|
||||
@@ -1604,17 +1604,17 @@ export class LayerManager {
|
||||
});
|
||||
|
||||
// 更新画布对象顺序
|
||||
this._rearrangeObjects();
|
||||
await this._rearrangeObjects();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新排列画布上的对象以匹配图层顺序
|
||||
* @private
|
||||
*/
|
||||
_rearrangeObjects() {
|
||||
async _rearrangeObjects() {
|
||||
if (this.layerSort) {
|
||||
// 使用LayerSort的高级排序
|
||||
this.layerSort.rearrangeObjects();
|
||||
await this.layerSort.rearrangeObjects();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1750,7 +1750,7 @@ export class LayerManager {
|
||||
layer.serializedObjects = layer.fabricObjects
|
||||
.map((obj) => {
|
||||
if (typeof obj.toObject === "function") {
|
||||
return obj.toObject(["id", "layerId", "layerName"]);
|
||||
return obj.toObject(["id", "layerId", "layerName", "fill_"]);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
@@ -1763,7 +1763,7 @@ export class LayerManager {
|
||||
if (layer.fabricObject) {
|
||||
layer.serializedBackgroundObject =
|
||||
typeof layer.fabricObject.toObject === "function"
|
||||
? layer.fabricObject.toObject(["id", "layerId", "layerName"])
|
||||
? layer.fabricObject.toObject(["id", "layerId", "layerName", "fill_"])
|
||||
: null;
|
||||
|
||||
delete layer.fabricObject;
|
||||
@@ -1793,7 +1793,7 @@ export class LayerManager {
|
||||
return layer.fabricObjects
|
||||
.map((obj) => {
|
||||
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;
|
||||
})
|
||||
.filter(Boolean);
|
||||
@@ -1839,13 +1839,14 @@ export class LayerManager {
|
||||
|
||||
// 存储到剪贴板
|
||||
this.clipboardData = layerCopy;
|
||||
console.log("复制图层:", layerCopy);
|
||||
const input = document.createElement("input");
|
||||
input.value = "aida_copy_canvas_layer: " + layer.name;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(input);
|
||||
console.log(`已复制图层:${layer.name}`);
|
||||
// console.log(`已复制图层:${layer.name}`);
|
||||
|
||||
return this.clipboardData;
|
||||
}
|
||||
@@ -1870,7 +1871,7 @@ export class LayerManager {
|
||||
|
||||
// 检查是否是唯一的普通图层
|
||||
const normalLayers = this.layers.value.filter((l) => !l.isBackground && !l.isFixed && !l.isFixedOther);
|
||||
console.log("普通图层:", normalLayers)
|
||||
// console.log("普通图层:", normalLayers)
|
||||
if (normalLayers.length <= 1) {
|
||||
console.warn("不能剪切唯一的普通图层");
|
||||
return null;
|
||||
@@ -1884,7 +1885,7 @@ export class LayerManager {
|
||||
layerCopy.serializedObjects = layer.fabricObjects
|
||||
.map((obj) =>
|
||||
typeof obj.toObject === "function"
|
||||
? obj.toObject(["id", "layerId", "layerName"])
|
||||
? obj.toObject(["id", "layerId", "layerName", "fill_"])
|
||||
: null
|
||||
)
|
||||
.filter(Boolean);
|
||||
@@ -1930,21 +1931,17 @@ export class LayerManager {
|
||||
// 更新对象交互性
|
||||
this.updateLayersObjectsInteractivity();
|
||||
|
||||
console.log(`已剪切图层:${layer.name}`);
|
||||
// console.log(`已剪切图层:${layer.name}`);
|
||||
|
||||
return this.clipboardData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 粘贴图层
|
||||
* @returns {string} 新创建的图层ID
|
||||
*/
|
||||
/**
|
||||
* 粘贴图层
|
||||
* @returns {string} 新创建的图层ID
|
||||
*/
|
||||
async pasteLayer(event) {
|
||||
console.log("剪贴板数据:", this.clipboardData,event);
|
||||
// console.log("剪贴板数据:", this.clipboardData,event);
|
||||
if (!this.clipboardData) {
|
||||
console.error("剪贴板中没有图层数据");
|
||||
return null;
|
||||
@@ -2099,7 +2096,7 @@ export class LayerManager {
|
||||
// 重新初始化基本图层
|
||||
this.initializeLayers();
|
||||
|
||||
console.log("已清空画布");
|
||||
// console.log("已清空画布");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2312,7 +2309,7 @@ export class LayerManager {
|
||||
});
|
||||
|
||||
if (emptyLayerIds.length > 0) {
|
||||
console.log(`已清理 ${emptyLayerIds.length} 个空图层`);
|
||||
// console.log(`已清理 ${emptyLayerIds.length} 个空图层`);
|
||||
}
|
||||
|
||||
return emptyLayerIds;
|
||||
@@ -2344,7 +2341,7 @@ export class LayerManager {
|
||||
this.canvasManager = null;
|
||||
this.toolManager = null;
|
||||
|
||||
console.log("LayerManager 已销毁");
|
||||
// console.log("LayerManager 已销毁");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2652,13 +2649,13 @@ export class LayerManager {
|
||||
// 启用红绿图模式
|
||||
enableRedGreenMode() {
|
||||
this.isRedGreenMode = true;
|
||||
console.log("图层管理器:红绿图模式已启用");
|
||||
// console.log("图层管理器:红绿图模式已启用");
|
||||
}
|
||||
|
||||
// 禁用红绿图模式
|
||||
disableRedGreenMode() {
|
||||
this.isRedGreenMode = false;
|
||||
console.log("图层管理器:红绿图模式已禁用");
|
||||
// console.log("图层管理器:红绿图模式已禁用");
|
||||
}
|
||||
|
||||
// 检查是否为红绿图模式
|
||||
@@ -2708,14 +2705,14 @@ export class LayerManager {
|
||||
: LayerSortUtils.shouldUseAsyncProcessing(objectsCount, layersCount);
|
||||
|
||||
if (shouldUseAsync) {
|
||||
console.log(
|
||||
`使用异步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
|
||||
);
|
||||
// console.log(
|
||||
// `使用异步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
|
||||
// );
|
||||
return this.layerSort.rearrangeObjectsAsync();
|
||||
} else {
|
||||
console.log(
|
||||
`使用同步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
|
||||
);
|
||||
// console.log(
|
||||
// `使用同步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
|
||||
// );
|
||||
this.layerSort.rearrangeObjects();
|
||||
}
|
||||
}
|
||||
@@ -2735,7 +2732,7 @@ export class LayerManager {
|
||||
const result = this.layerSort.smartSort(targetLayerIds);
|
||||
|
||||
if (result) {
|
||||
console.log("智能排序完成");
|
||||
// console.log("智能排序完成");
|
||||
// 更新对象交互性
|
||||
this.updateLayersObjectsInteractivity();
|
||||
}
|
||||
@@ -2761,9 +2758,9 @@ export class LayerManager {
|
||||
const stats = this.layerSort.optimizeLayerStructure();
|
||||
|
||||
if (stats.removedEmptyLayers > 0 || stats.reorderedLayers > 0) {
|
||||
console.log(
|
||||
`图层结构优化完成: 清理空图层 ${stats.removedEmptyLayers} 个, 重新排序 ${stats.reorderedLayers} 个图层`
|
||||
);
|
||||
// console.log(
|
||||
// `图层结构优化完成: 清理空图层 ${stats.removedEmptyLayers} 个, 重新排序 ${stats.reorderedLayers} 个图层`
|
||||
// );
|
||||
// 更新对象交互性
|
||||
this.updateLayersObjectsInteractivity();
|
||||
}
|
||||
@@ -2838,9 +2835,9 @@ export class LayerManager {
|
||||
});
|
||||
|
||||
if (result) {
|
||||
console.log(
|
||||
`图层 ${layerId} - oldIndex: ${oldIndex} 已移动到位置 ${newIndex}`
|
||||
);
|
||||
// console.log(
|
||||
// `图层 ${layerId} - oldIndex: ${oldIndex} 已移动到位置 ${newIndex}`
|
||||
// );
|
||||
// 更新对象交互性
|
||||
// this.updateLayersObjectsInteractivity();
|
||||
}
|
||||
@@ -2917,9 +2914,9 @@ export class LayerManager {
|
||||
const result = this.layerSort.reorderLayers(oldIndex, newIndex, layerId);
|
||||
|
||||
if (result) {
|
||||
console.log(
|
||||
`高级排序完成: 图层 ${layerId} 从位置 ${oldIndex} 移动到 ${newIndex}`
|
||||
);
|
||||
// console.log(
|
||||
// `高级排序完成: 图层 ${layerId} 从位置 ${oldIndex} 移动到 ${newIndex}`
|
||||
// );
|
||||
// 更新对象交互性
|
||||
this.updateLayersObjectsInteractivity();
|
||||
}
|
||||
@@ -2944,9 +2941,9 @@ export class LayerManager {
|
||||
);
|
||||
|
||||
if (result) {
|
||||
console.log(
|
||||
`高级子图层排序完成: 图层 ${layerId} 在父图层 ${parentId} 中从位置 ${oldIndex} 移动到 ${newIndex}`
|
||||
);
|
||||
// console.log(
|
||||
// `高级子图层排序完成: 图层 ${layerId} 在父图层 ${parentId} 中从位置 ${oldIndex} 移动到 ${newIndex}`
|
||||
// );
|
||||
// 重新排列画布对象
|
||||
this.rearrangeCanvasObjects();
|
||||
}
|
||||
@@ -2971,7 +2968,7 @@ export class LayerManager {
|
||||
// 重新排列画布对象
|
||||
this.rearrangeCanvasObjects();
|
||||
|
||||
console.log("使用LayerSort工具完成图层排序");
|
||||
// console.log("使用LayerSort工具完成图层排序");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2979,7 +2976,7 @@ export class LayerManager {
|
||||
* 当图层顺序发生变化后调用此方法确保画布对象顺序正确
|
||||
*/
|
||||
forceRebuildCanvasOrder() {
|
||||
console.log("强制重建画布对象顺序");
|
||||
// console.log("强制重建画布对象顺序");
|
||||
|
||||
if (this.layerSort) {
|
||||
// 使用LayerSort的高级排序
|
||||
@@ -3010,7 +3007,7 @@ export class LayerManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(`开始批量重新排序 ${reorderOperations.length} 个图层`);
|
||||
// console.log(`开始批量重新排序 ${reorderOperations.length} 个图层`);
|
||||
|
||||
let allSuccessful = true;
|
||||
|
||||
@@ -3042,7 +3039,7 @@ export class LayerManager {
|
||||
}
|
||||
|
||||
if (allSuccessful) {
|
||||
console.log("批量图层排序完成");
|
||||
// console.log("批量图层排序完成");
|
||||
} else {
|
||||
console.warn("批量图层排序部分失败");
|
||||
}
|
||||
@@ -3079,11 +3076,11 @@ export class LayerManager {
|
||||
// 执行命令
|
||||
if (this.commandManager) {
|
||||
const result = await this.commandManager.execute(command);
|
||||
result && console.log(`✅ 成功合并组图层: ${groupLayer.name}`);
|
||||
// result && console.log(`✅ 成功合并组图层: ${groupLayer.name}`);
|
||||
return result;
|
||||
} else {
|
||||
const result = await command.execute();
|
||||
result && console.log(`✅ 成功合并组图层: ${groupLayer.name}`);
|
||||
// result && console.log(`✅ 成功合并组图层: ${groupLayer.name}`);
|
||||
return result || [];
|
||||
}
|
||||
}
|
||||
@@ -3132,13 +3129,13 @@ export class LayerManager {
|
||||
if (this.commandManager) {
|
||||
const result = await this.commandManager.execute(command);
|
||||
if (result) {
|
||||
console.log(`✅ 成功栅格化图层: ${targetLayer.name}`);
|
||||
// console.log(`✅ 成功栅格化图层: ${targetLayer.name}`);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
const result = await command.execute();
|
||||
if (result) {
|
||||
console.log(`✅ 成功栅格化图层: ${targetLayer.name}`);
|
||||
// console.log(`✅ 成功栅格化图层: ${targetLayer.name}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -3209,13 +3206,13 @@ export class LayerManager {
|
||||
if (this.commandManager) {
|
||||
const result = await this.commandManager.execute(command);
|
||||
if (result) {
|
||||
console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
||||
// console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
const result = await command.execute();
|
||||
if (result) {
|
||||
console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
||||
// console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -3259,7 +3256,7 @@ export class LayerManager {
|
||||
// 执行命令
|
||||
const result = await command.execute(false);
|
||||
if (result) {
|
||||
console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
||||
// console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -3294,12 +3291,12 @@ export class LayerManager {
|
||||
const isSginleObject = e.target === activeSelection?._objects?.[0];
|
||||
if (e.target === activeSelection || isSginleObject) {
|
||||
hasMoved = false; // 重置移动状态
|
||||
console.log("🎯 开始移动组选择对象");
|
||||
// console.log("🎯 开始移动组选择对象");
|
||||
// 记录遮罩初始位置
|
||||
console.log(
|
||||
"🖼️ 记录遮罩初始位置",
|
||||
`${layer.clippingMask.left || 0}, ${layer.clippingMask.top || 0}`
|
||||
);
|
||||
// console.log(
|
||||
// "🖼️ 记录遮罩初始位置",
|
||||
// `${layer.clippingMask.left || 0}, ${layer.clippingMask.top || 0}`
|
||||
// );
|
||||
// 记录初始位置
|
||||
initialLeft = isSginleObject ? e.target.left : activeSelection.left;
|
||||
initialTop = isSginleObject ? e.target.top : activeSelection.top;
|
||||
@@ -3366,12 +3363,12 @@ export class LayerManager {
|
||||
const isSginleObject = e.target === activeSelection?._objects?.[0];
|
||||
if (isSginleObject) {
|
||||
// 如果是单个对象,不处理
|
||||
console.log("🚫 单个对象不处理移动完成");
|
||||
// console.log("🚫 单个对象不处理移动完成");
|
||||
hasMoved = false; // 重置移动状态
|
||||
return;
|
||||
}
|
||||
if ((target === activeSelection || isSginleObject) && hasMoved) {
|
||||
console.log("✅ 组选择对象移动完成");
|
||||
// console.log("✅ 组选择对象移动完成");
|
||||
|
||||
// 计算最终移动距离
|
||||
const deltaX = target.left - initialLeft;
|
||||
@@ -3410,7 +3407,7 @@ export class LayerManager {
|
||||
// 鼠标抬起事件处理 - 备用方案
|
||||
const handleMouseUp = (e) => {
|
||||
if (hasMoved && this.canvas.getActiveObject() === activeSelection) {
|
||||
console.log("🖱️ 鼠标抬起 - 备用移动完成处理");
|
||||
// console.log("🖱️ 鼠标抬起 - 备用移动完成处理");
|
||||
handleModified(e);
|
||||
}
|
||||
};
|
||||
@@ -3424,7 +3421,7 @@ export class LayerManager {
|
||||
this.canvas.off("selection:cleared", cleanup);
|
||||
this.canvas.off("selection:updated", cleanup);
|
||||
|
||||
console.log("🧹 清理组遮罩移动同步事件监听器");
|
||||
// console.log("🧹 清理组遮罩移动同步事件监听器");
|
||||
};
|
||||
|
||||
// 绑定事件监听器
|
||||
@@ -3437,7 +3434,7 @@ export class LayerManager {
|
||||
this.canvas.on("selection:cleared", cleanup);
|
||||
this.canvas.on("selection:updated", cleanup);
|
||||
|
||||
console.log("🎨 已设置组遮罩移动同步 - 使用 object:modified 事件");
|
||||
// console.log("🎨 已设置组遮罩移动同步 - 使用 object:modified 事件");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,7 @@ export class ThumbnailManager {
|
||||
const { layer } = findLayerRecursively(this.layers.value, layerId);
|
||||
|
||||
if (!fabricObjects || fabricObjects.length === 0) {
|
||||
console.warn("⚠️ 无法生成缩略图:没有可栅格化的对象 返回空缩略图");
|
||||
// console.warn("⚠️ 无法生成缩略图:没有可栅格化的对象 返回空缩略图");
|
||||
// 如果没有对象,返回默认缩略图
|
||||
if (layer) {
|
||||
layer.thumbnailUrl = this.defaultThumbnail; // 更新图层对象的缩略图
|
||||
@@ -123,7 +123,7 @@ export class ThumbnailManager {
|
||||
*/
|
||||
_collectLayersAndObjects(layerId) {
|
||||
if (!layerId) {
|
||||
console.warn("⚠️ 无效的图层ID,无法收集对象");
|
||||
// console.warn("⚠️ 无效的图层ID,无法收集对象");
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -199,17 +199,17 @@ export class ThumbnailManager {
|
||||
// 提取排序后的对象
|
||||
const objectsToRasterize = objectsWithZIndex.map((item) => item.object);
|
||||
|
||||
console.log(
|
||||
`📊 收集到 ${layersToRasterize.length} 个图层,${objectsToRasterize.length} 个对象进行栅格化`
|
||||
);
|
||||
console.log(
|
||||
"🔢 对象z-index顺序:",
|
||||
objectsWithZIndex.map((item) => ({
|
||||
id: item.object.id,
|
||||
type: item.object.type,
|
||||
zIndex: item.zIndex,
|
||||
}))
|
||||
);
|
||||
// console.log(
|
||||
// `📊 收集到 ${layersToRasterize.length} 个图层,${objectsToRasterize.length} 个对象进行栅格化`
|
||||
// );
|
||||
// console.log(
|
||||
// "🔢 对象z-index顺序:",
|
||||
// objectsWithZIndex.map((item) => ({
|
||||
// id: item.object.id,
|
||||
// type: item.object.type,
|
||||
// zIndex: item.zIndex,
|
||||
// }))
|
||||
// );
|
||||
|
||||
return objectsToRasterize;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ export class ToolManager {
|
||||
this.activeTool = options.activeTool || {
|
||||
value: OperationType.SELECT,
|
||||
};
|
||||
this.props = options.props;
|
||||
|
||||
// 红绿图模式状态
|
||||
this.isRedGreenMode = false;
|
||||
@@ -196,20 +197,20 @@ export class ToolManager {
|
||||
[OperationType.PART_RECTANGLE]: {
|
||||
name: "部件选取工具-矩形",
|
||||
icon: "part",
|
||||
cursor: "default",
|
||||
setup: this.setupPartTool.bind(this),
|
||||
cursor: "crosshair",
|
||||
setup: this.setupPartRectangleTool.bind(this),
|
||||
},
|
||||
[OperationType.PART_BRUSH]: {
|
||||
name: "部件选取工具-画笔",
|
||||
icon: "part",
|
||||
cursor: "default",
|
||||
setup: this.setupPartTool.bind(this),
|
||||
setup: this.setupPartBrushTool.bind(this),
|
||||
},
|
||||
[OperationType.PART_ERASER]: {
|
||||
name: "部件选取工具-橡皮擦",
|
||||
icon: "part",
|
||||
cursor: "default",
|
||||
setup: this.setupPartTool.bind(this),
|
||||
setup: this.setupPartEraserTool.bind(this),
|
||||
},
|
||||
|
||||
// 红绿图模式专用工具
|
||||
@@ -419,15 +420,18 @@ export class ToolManager {
|
||||
|
||||
// 设置工具特定的状态
|
||||
if (tool && typeof tool.setup === "function") {
|
||||
console.log(`画布切换工具:${tool.name}(${toolId})`)
|
||||
// console.log(`画布切换工具:${tool.name}(${toolId})`)
|
||||
this.canvas.toolId = toolId;
|
||||
tool.setup();
|
||||
tool.setup(true);
|
||||
}
|
||||
|
||||
// 通知选区管理器工具已改变
|
||||
if (this.canvasManager && this.canvasManager.selectionManager) {
|
||||
this.canvasManager.selectionManager.setCurrentTool(toolId);
|
||||
}
|
||||
if (this.canvasManager && this.canvasManager.partManager) {
|
||||
this.canvasManager.partManager.setCurrentTool(toolId);
|
||||
}
|
||||
|
||||
// 通知观察者
|
||||
this.notifyObservers(toolId);
|
||||
@@ -478,7 +482,7 @@ export class ToolManager {
|
||||
|
||||
// 如有必要,可以调用当前工具的setup方法来全面恢复状态
|
||||
if (tool && typeof tool.setup === "function") {
|
||||
tool.setup();
|
||||
tool.setup(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,7 +695,7 @@ export class ToolManager {
|
||||
|
||||
// // 设置矩形选区模式
|
||||
// // 这里需要具体的矩形选区工具实现
|
||||
console.log("矩形选区工具已激活");
|
||||
// console.log("矩形选区工具已激活");
|
||||
|
||||
if (this.canvasManager && this.canvasManager.selectionManager) {
|
||||
this.canvasManager.selectionManager.setCurrentTool(
|
||||
@@ -703,16 +707,73 @@ export class ToolManager {
|
||||
/**
|
||||
* 设置部件选取工具
|
||||
*/
|
||||
setupPartTool() {
|
||||
setupPartTool(isExecute = false) {
|
||||
if (!this.canvas) return;
|
||||
if (this.checkToolCanOperateSelectedObject()) return;
|
||||
this.canvas.isDrawingMode = false;
|
||||
this.canvas.selection = false;
|
||||
|
||||
if (this.canvasManager && this.canvasManager.partManager) {
|
||||
if (!isExecute && this.canvasManager && this.canvasManager.partManager) {
|
||||
this.canvasManager.partManager.setCurrentTool(OperationType.PART);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置部件选取工具--矩形
|
||||
*/
|
||||
setupPartRectangleTool(isExecute = false) {
|
||||
if (!this.canvas) return;
|
||||
this.canvas.isDrawingMode = false;
|
||||
this.canvas.selection = false;
|
||||
if (!isExecute && this.canvasManager && this.canvasManager.partManager) {
|
||||
this.canvasManager.partManager.setCurrentTool(OperationType.PART_RECTANGLE);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置部件选取工具--画笔
|
||||
*/
|
||||
setupPartBrushTool(isExecute = false) {
|
||||
if (!this.canvas) return;
|
||||
this.canvas.isDrawingMode = true;
|
||||
this.canvas.selection = false;
|
||||
if (!isExecute && this.canvasManager && this.canvasManager.partManager) {
|
||||
this.canvasManager.partManager.setCurrentTool(OperationType.PART_BRUSH);
|
||||
}
|
||||
const greenColor = "#0f0";
|
||||
// 确保有笔刷管理器
|
||||
if (this.brushManager) {
|
||||
// 设置绿色笔刷
|
||||
this.brushManager.setBrushColor(greenColor); // 纯绿色
|
||||
this.brushManager.setBrushOpacity(200/255); // 完全不透明
|
||||
this.brushManager.setBrushType("pencil"); // 铅笔类型
|
||||
|
||||
// 更新笔刷大小(使用当前大小)
|
||||
if (BrushStore && BrushStore.state.size) {
|
||||
this.brushManager.setBrushSize(BrushStore.state.size);
|
||||
}
|
||||
|
||||
// 更新应用到画布
|
||||
this.brushManager.updateBrush();
|
||||
}
|
||||
|
||||
// 启用笔刷指示器并设置绿色
|
||||
this._enableBrushIndicator(greenColor);
|
||||
}
|
||||
/**
|
||||
* 设置部件选取工具--橡皮擦
|
||||
*/
|
||||
setupPartEraserTool(isExecute = false) {
|
||||
if (!this.canvas) return;
|
||||
this.canvas.isDrawingMode = true;
|
||||
this.canvas.selection = false;
|
||||
if (this.brushManager) {
|
||||
this.brushManager.createEraser();
|
||||
}
|
||||
// 启用笔刷指示器
|
||||
this._enableBrushIndicator();
|
||||
|
||||
if (!isExecute && this.canvasManager && this.canvasManager.partManager) {
|
||||
this.canvasManager.partManager.setCurrentTool(OperationType.PART_ERASER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置波浪工具
|
||||
@@ -857,7 +918,7 @@ export class ToolManager {
|
||||
this._rasterizeLayerForLiquify(layerId);
|
||||
},
|
||||
onCancel: () => {
|
||||
console.log("用户取消了栅格化操作");
|
||||
// console.log("用户取消了栅格化操作");
|
||||
// 用户取消,触发液化面板显示事件但不能液化
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("showLiquifyPanel", {
|
||||
@@ -1249,7 +1310,7 @@ export class ToolManager {
|
||||
*/
|
||||
showTextEditor(textObject, layer) {
|
||||
// 这个方法将在TextEditorPanel组件实现后调用
|
||||
console.log("显示文本编辑面板", textObject, layer);
|
||||
// console.log("显示文本编辑面板", textObject, layer);
|
||||
// 将发出一个事件,让Vue组件捕获并显示编辑面板
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("showTextEditor", {
|
||||
@@ -1265,7 +1326,7 @@ export class ToolManager {
|
||||
*/
|
||||
hideTextEditor() {
|
||||
// 这个方法将在TextEditorPanel组件实现后调用
|
||||
console.log("隐藏文本编辑面板");
|
||||
// console.log("隐藏文本编辑面板");
|
||||
// 将发出一个事件,让Vue组件捕获并隐藏编辑面板
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("hideTextEditor", {
|
||||
@@ -1286,7 +1347,7 @@ export class ToolManager {
|
||||
if (this.brushIndicator) {
|
||||
this.brushIndicator.dispose();
|
||||
this.brushIndicator = null;
|
||||
console.log("笔刷指示器已清理");
|
||||
// console.log("笔刷指示器已清理");
|
||||
}
|
||||
|
||||
// 移除文本编辑相关事件监听器
|
||||
@@ -1311,7 +1372,7 @@ export class ToolManager {
|
||||
this.canvas.isDrawingMode = false;
|
||||
this.canvas.selection = false;
|
||||
|
||||
console.log("文本工具已激活");
|
||||
// console.log("文本工具已激活");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1387,7 +1448,7 @@ export class ToolManager {
|
||||
// 切换到红色笔刷工具作为默认工具
|
||||
this.setTool(OperationType.RED_BRUSH);
|
||||
|
||||
console.log("工具管理器已进入红绿图模式");
|
||||
// console.log("工具管理器已进入红绿图模式");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1400,7 +1461,7 @@ export class ToolManager {
|
||||
// 切换回选择工具
|
||||
this.setTool(OperationType.SELECT);
|
||||
|
||||
console.log("工具管理器已退出红绿图模式");
|
||||
// console.log("工具管理器已退出红绿图模式");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1458,7 +1519,7 @@ export class ToolManager {
|
||||
// 更新指示器颜色
|
||||
this.brushIndicator.updateColor(brushColor);
|
||||
|
||||
console.log(`笔刷指示器已启用,大小: ${brushSize}, 颜色: ${brushColor}`);
|
||||
// console.log(`笔刷指示器已启用,大小: ${brushSize}, 颜色: ${brushColor}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1469,7 +1530,7 @@ export class ToolManager {
|
||||
if (!this.brushIndicator) return;
|
||||
|
||||
this.brushIndicator.disable();
|
||||
console.log("笔刷指示器已禁用");
|
||||
// console.log("笔刷指示器已禁用");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1480,7 +1541,7 @@ export class ToolManager {
|
||||
if (!this.brushIndicator) return;
|
||||
|
||||
this.brushIndicator.updateSize(size);
|
||||
console.log(`笔刷指示器大小已更新为: ${size}`);
|
||||
// console.log(`笔刷指示器大小已更新为: ${size}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1491,7 +1552,7 @@ export class ToolManager {
|
||||
if (!this.brushIndicator) return;
|
||||
|
||||
this.brushIndicator.updateColor(color);
|
||||
console.log(`笔刷指示器颜色已更新为: ${color}`);
|
||||
// console.log(`笔刷指示器颜色已更新为: ${color}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1566,7 +1627,9 @@ export class ToolManager {
|
||||
OperationType.ERASER,
|
||||
OperationType.RED_BRUSH,
|
||||
OperationType.GREEN_BRUSH,
|
||||
OperationType.LIQUIFY,
|
||||
OperationType.LIQUIFY,
|
||||
OperationType.PART_BRUSH,
|
||||
OperationType.PART_ERASER,
|
||||
];
|
||||
|
||||
return brushTools.includes(currentTool);
|
||||
|
||||
@@ -299,7 +299,7 @@ export class CommandManager {
|
||||
this.undoStack = [];
|
||||
this.redoStack = [];
|
||||
this._notifyStateChange();
|
||||
console.log("📝 命令历史已清空");
|
||||
// console.log("📝 命令历史已清空");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ import { OperationType, OperationTypes } from "../../utils/layerHelper";
|
||||
export class CanvasEventManager {
|
||||
constructor(canvas, options = {}) {
|
||||
this.canvas = canvas;
|
||||
this.canvasManager = options.canvasManager;
|
||||
this.toolManager = options.toolManager || null;
|
||||
this.animationManager = options.animationManager;
|
||||
this.thumbnailManager = options.thumbnailManager;
|
||||
@@ -485,7 +486,7 @@ export class CanvasEventManager {
|
||||
|
||||
// 调试信息
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log("iPad touchstart:", e.touches.length, "fingers");
|
||||
// console.log("iPad touchstart:", e.touches.length, "fingers");
|
||||
}
|
||||
|
||||
if (e.touches.length === 2) {
|
||||
@@ -500,11 +501,11 @@ export class CanvasEventManager {
|
||||
this.touchState.zoomCenter = { x: centerX, y: centerY };
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log("iPad双指缩放开始:", {
|
||||
distance: lastTouchDistance,
|
||||
zoom: lastZoom,
|
||||
center: this.touchState.zoomCenter,
|
||||
});
|
||||
// console.log("iPad双指缩放开始:", {
|
||||
// distance: lastTouchDistance,
|
||||
// zoom: lastZoom,
|
||||
// center: this.touchState.zoomCenter,
|
||||
// });
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
@@ -554,14 +555,14 @@ export class CanvasEventManager {
|
||||
const clampedZoom = Math.max(0.1, Math.min(5, newZoom));
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log("iPad双指缩放中:", {
|
||||
currentDistance,
|
||||
lastTouchDistance,
|
||||
scale,
|
||||
currentZoom,
|
||||
newZoom,
|
||||
clampedZoom,
|
||||
});
|
||||
// console.log("iPad双指缩放中:", {
|
||||
// currentDistance,
|
||||
// lastTouchDistance,
|
||||
// scale,
|
||||
// currentZoom,
|
||||
// newZoom,
|
||||
// clampedZoom,
|
||||
// });
|
||||
}
|
||||
|
||||
// 使用缩放中心点进行缩放
|
||||
@@ -691,7 +692,9 @@ export class CanvasEventManager {
|
||||
// 清除临时状态记录
|
||||
delete activeObj._initialTransformState;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
this.canvasManager.changeCanvas();
|
||||
}
|
||||
|
||||
if (this.thumbnailManager && e.target) {
|
||||
if (e.target.id) {
|
||||
@@ -839,7 +842,7 @@ export class CanvasEventManager {
|
||||
const hasNewImage = !!fabricImage;
|
||||
|
||||
if (!hasExistingObjects && !hasNewImage) {
|
||||
console.log("没有对象需要合并");
|
||||
// console.log("没有对象需要合并");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -851,7 +854,7 @@ export class CanvasEventManager {
|
||||
|
||||
// 执行高保真合并操作
|
||||
try {
|
||||
console.log(`开始合并图层 ${activeLayer.name} 中的对象为组...`);
|
||||
// console.log(`开始合并图层 ${activeLayer.name} 中的对象为组...`);
|
||||
|
||||
const command = await this.layerManager.LayerObjectsToGroup(
|
||||
activeLayer,
|
||||
@@ -869,7 +872,7 @@ export class CanvasEventManager {
|
||||
|
||||
// 降级处理:如果合并失败,至少保证新图像能添加到图层
|
||||
if (fabricImage && this.layerManager) {
|
||||
console.log("执行降级处理:直接添加图像到图层");
|
||||
// console.log("执行降级处理:直接添加图像到图层");
|
||||
this.layerManager.addObjectToLayer(fabricImage, activeLayer.id);
|
||||
}
|
||||
}
|
||||
@@ -975,6 +978,13 @@ export class CanvasEventManager {
|
||||
// 添加调试日志(可选)
|
||||
// console.log(`捕获对象 ${obj.id} (${obj.type}) 的初始变换状态`);
|
||||
}
|
||||
const arrs = [];
|
||||
if (e.target._objects) {
|
||||
e.target._objects.forEach((v) => arrs.push(v));
|
||||
} else {
|
||||
arrs.push(e.target);
|
||||
}
|
||||
this.canvasManager.beforeChangeCanvas(arrs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1011,15 +1021,15 @@ export class CanvasEventManager {
|
||||
|
||||
// 调试日志 - 仅在开发环境输出
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log("设备检测结果:", {
|
||||
userAgent,
|
||||
platform,
|
||||
isMobile,
|
||||
isTablet,
|
||||
isDesktop,
|
||||
hasTouchSupport,
|
||||
maxTouchPoints: navigator.maxTouchPoints,
|
||||
});
|
||||
// console.log("设备检测结果:", {
|
||||
// userAgent,
|
||||
// platform,
|
||||
// isMobile,
|
||||
// isTablet,
|
||||
// isDesktop,
|
||||
// hasTouchSupport,
|
||||
// maxTouchPoints: navigator.maxTouchPoints,
|
||||
// });
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||