Compare commits
302 Commits
bc7099cce2
...
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 | ||
|
|
7bf1a0bd57 | ||
|
|
810dd2351b | ||
|
|
e42975159f | ||
|
|
c1cff1d61b | ||
|
|
dbe4557dc3 | ||
|
|
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 = 'https://www.api.aida.com.hk'
|
||||||
# 徐佩
|
# 徐佩
|
||||||
# VITE_APP_BASE_URL = 'http://192.168.31.118:5567'
|
# 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'
|
# 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 = 'https://develop.api.aida.com.hk'
|
||||||
|
|
||||||
# VITE_APP_BASE_URL = 'http://localhost:22170'
|
# 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/sloganFamily.css">
|
||||||
<link rel="stylesheet" href="/css/pingfang.css">
|
<link rel="stylesheet" href="/css/pingfang.css">
|
||||||
|
<link rel="stylesheet" href="/css/fonts/fontFamily.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<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"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
<svg id="_图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 184.06 163.52">
|
||||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
<defs>
|
||||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
<style>
|
||||||
width="103.000000pt" height="92.000000pt" viewBox="0 0 103.000000 92.000000"
|
.cls-1, .cls-2 {
|
||||||
preserveAspectRatio="xMidYMid meet">
|
fill: #333;
|
||||||
|
}
|
||||||
|
|
||||||
<g transform="translate(0.000000,92.000000) scale(0.100000,-0.100000)"
|
.cls-2 {
|
||||||
fill="#000000" stroke="none">
|
stroke: #333;
|
||||||
<path d="M365 895 c-5 -2 -36 -6 -67 -10 -45 -5 -58 -10 -59 -23 0 -11 -2 -12
|
stroke-miterlimit: 10;
|
||||||
-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
|
</style>
|
||||||
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
|
</defs>
|
||||||
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
|
<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"/>
|
||||||
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
|
<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"/>
|
||||||
-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
|
<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"/>
|
||||||
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
|
<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"/>
|
||||||
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
|
<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"/>
|
||||||
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
|
<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"/>
|
||||||
-38 12 -71 19 -74 16 -3 -3 -11 0 -18 6 -8 6 -20 9 -28 6 -8 -3 -16 -2 -18 3
|
<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"/>
|
||||||
-5 15 -154 30 -286 29 -70 0 -131 -2 -137 -4z m21 -30 c-6 -18 3 -47 14 -40 4
|
<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"/>
|
||||||
2 18 -7 30 -20 27 -29 17 -34 -14 -7 -20 16 -20 16 -7 -1 23 -29 66 -47 112
|
<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"/>
|
||||||
-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
|
<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"/>
|
||||||
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
|
<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"/>
|
||||||
14 -16 34 -12 15 2 36 1 47 -4 16 -7 14 -8 -10 -5 l-30 5 32 -14 c39 -18 39
|
<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"/>
|
||||||
-19 14 -83 -15 -38 -17 -52 -8 -61 9 -9 8 -11 -7 -5 -16 6 -18 4 -12 -19 3
|
<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"/>
|
||||||
-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
|
<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"/>
|
||||||
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>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 2.6 KiB |
31
src/assets/icons/CPoint.svg
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?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="96.000000pt" height="96.000000pt" viewBox="0 0 96.000000 96.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
|
||||||
|
<g transform="translate(0.000000,96.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M346 936 c-21 -13 -49 -41 -62 -62 -41 -67 -27 -180 27 -218 47 -32
|
||||||
|
53 -12 12 40 -71 94 -2 229 116 229 37 0 58 -7 87 -28 32 -22 40 -24 42 -12
|
||||||
|
14 64 -142 100 -222 51z"/>
|
||||||
|
<path d="M368 877 c-33 -28 -48 -57 -48 -96 0 -39 9 -61 26 -61 10 0 14 13 14
|
||||||
|
46 0 57 12 79 50 93 61 21 110 -22 110 -96 0 -48 14 -56 31 -19 31 67 -35 156
|
||||||
|
-114 156 -29 0 -50 -7 -69 -23z"/>
|
||||||
|
<path d="M580 794 c0 -58 -9 -84 -43 -121 -19 -21 -19 -23 -2 -29 34 -13 85
|
||||||
|
75 85 148 0 37 -10 58 -26 58 -10 0 -14 -15 -14 -56z"/>
|
||||||
|
<path d="M410 749 c-13 -6 -28 -15 -32 -22 -4 -7 -8 -106 -8 -222 l-1 -210
|
||||||
|
-27 34 c-62 80 -89 101 -126 101 -27 0 -39 -6 -52 -25 -15 -24 -15 -28 0 -68
|
||||||
|
21 -53 78 -123 94 -113 18 11 16 17 -28 75 -44 58 -48 72 -25 91 21 18 54 -6
|
||||||
|
93 -68 31 -47 74 -78 93 -67 5 4 9 101 9 224 0 225 3 241 42 241 10 0 19 -1
|
||||||
|
19 -2 1 -2 5 -86 8 -188 5 -165 8 -185 24 -188 15 -3 17 5 17 61 0 72 17 100
|
||||||
|
45 77 10 -9 15 -32 15 -77 0 -56 2 -64 18 -61 12 2 16 11 14 34 -5 48 14 86
|
||||||
|
41 82 20 -3 22 -9 25 -66 3 -53 6 -63 20 -60 12 2 19 16 22 43 4 32 10 41 27
|
||||||
|
43 36 5 46 -36 39 -166 -6 -127 -19 -160 -75 -195 -48 -29 -176 -34 -246 -10
|
||||||
|
-48 18 -56 25 -130 128 -16 22 -25 26 -37 19 -15 -8 -11 -18 31 -75 78 -105
|
||||||
|
94 -113 236 -117 119 -3 121 -3 167 27 69 44 82 74 86 214 6 179 -8 221 -72
|
||||||
|
216 -22 -2 -39 4 -55 20 -17 17 -32 22 -55 19 -19 -2 -38 2 -45 9 -6 7 -25 13
|
||||||
|
-40 13 l-29 0 -4 100 c-3 103 -11 122 -53 133 -11 3 -31 1 -45 -4z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 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 |
@@ -2,92 +2,123 @@
|
|||||||
"eventsList": [
|
"eventsList": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"title":"Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face!",
|
"title": "Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face!",
|
||||||
"imgUrl": "/image/events/workshop-En.jpg"
|
"imgUrl": "/image/events/workshop-En.jpg"
|
||||||
},{
|
},
|
||||||
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"title":"AiDA X SFT AI Fashion Award 2024",
|
"title": "AiDA X SFT AI Fashion Award 2024",
|
||||||
"imgUrl": "/image/events/Fashion-Award-2024.png"
|
"imgUrl": "/image/events/Fashion-Award-2024.png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"eventsItem":[
|
"eventsItem": [
|
||||||
{
|
{
|
||||||
"id":1,
|
"id": 3,
|
||||||
"title":"Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face!",
|
"title": "AiDA Global Design Awards 2026",
|
||||||
"imgUrl": "/image/events/workshop-En.jpg",
|
"imgUrl": "/image/events/award-poster.gif",
|
||||||
"textList":[
|
"textList": [
|
||||||
{
|
{
|
||||||
"paragraph":[
|
"paragraph": [
|
||||||
{
|
{
|
||||||
"text":"🎨AiDA Workshop!"
|
"text": "Click the “View Details” button for more information and to join the competition! The AiDA Global Design Award 2026 is an international design competition hosted by 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":"The process is simple: use AiDA to post your design work on the 'Gallery', and the one with the most likes(at least 20 likes) will be invited to the AiDA Workshop offline event in Hong Kong on November 14th, to exchange ideas with the Royal College of Art (RCA), Jae Lim, co-founder of the renowned fashion brand BESFXXK, and outstanding designers! "
|
"paragraph": [
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
{
|
||||||
"text":"<b>⚠️ATTENTION❗❗</b>"
|
"text": "Participants have the opportunity to compete for cash prizes totaling up to US$9,000, gain global media exposure showcased by top international platforms, and connect with designers and industry leaders worldwide. Finalists will also attend an exclusive award ceremony in Hong Kong, with travel support provided, allowing them to showcase their talent, network with professionals, and celebrate their achievements on an international stage."
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
|
||||||
"text":"1. Add the tag in the work description #AiDAworkshop_2024"
|
|
||||||
},{
|
|
||||||
"text":"2. One winner only"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
|
||||||
"text":"<b>🤩Code-Create will provide (Terms and conditions apply):</b>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
|
||||||
"text":"✅Round-trip transportation fee (only within China)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
|
||||||
"text":"✅One night accommodation fee"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
|
||||||
"text":"⌛️Deadline: October 31, 2024"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id":2,
|
"id": 1,
|
||||||
"title":"AiDA X SFT AI Fashion Award 2024",
|
"title": "Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face!",
|
||||||
|
"imgUrl": "/image/events/workshop-En.jpg",
|
||||||
|
"textList": [
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "🎨AiDA Workshop!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "The process is simple: use AiDA to post your design work on the 'Gallery', and the one with the most likes(at least 20 likes) will be invited to the AiDA Workshop offline event in Hong Kong on November 14th, to exchange ideas with the Royal College of Art (RCA), Jae Lim, co-founder of the renowned fashion brand BESFXXK, and outstanding designers! "
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "<b>⚠️ATTENTION❗❗</b>"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "1. Add the tag in the work description #AiDAworkshop_2024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "2. One winner only"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "<b>🤩Code-Create will provide (Terms and conditions apply):</b>"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "✅Round-trip transportation fee (only within China)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "✅One night accommodation fee"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "⌛️Deadline: October 31, 2024"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"title": "AiDA X SFT AI Fashion Award 2024",
|
||||||
"imgUrl": "/image/events/Fashion-Award-2024.png",
|
"imgUrl": "/image/events/Fashion-Award-2024.png",
|
||||||
"textList":[
|
"textList": [
|
||||||
{
|
{
|
||||||
"paragraph":[
|
"paragraph": [
|
||||||
{
|
{
|
||||||
"text":"With the aim of inspiring students to innovate in fashion design using AI, Code-Create and The Hong Kong Polytechnic University School of Fashion and Textiles (SFT) have jointly launched the 'AiDA X SFT AI Fashion Award 2024'. This competition provides students with valuable practical AiDA experience, laying the foundation for the future fashion design industry and positioning them as pioneers in AI fashion."
|
"text": "With the aim of inspiring students to innovate in fashion design using AI, Code-Create and The Hong Kong Polytechnic University School of Fashion and Textiles (SFT) have jointly launched the 'AiDA X SFT AI Fashion Award 2024'. This competition provides students with valuable practical AiDA experience, laying the foundation for the future fashion design industry and positioning them as pioneers in AI fashion."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},{
|
},
|
||||||
"paragraph":[
|
|
||||||
{
|
{
|
||||||
"text":"The competition is open to all SFT students, with the winners having the chance to win cash prizes (up to 20,000 HKD), internship opportunity at BESFXXK (will work with the renowned designer, Mr Jae Hyuk Lim, for the BESFXXK collection, that will be featured at NY Fashion Week and Paris Fashion Week) and more surprises! Scan the QR code to learn more."
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "The competition is open to all SFT students, with the winners having the chance to win cash prizes (up to 20,000 HKD), internship opportunity at BESFXXK (will work with the renowned designer, Mr Jae Hyuk Lim, for the BESFXXK collection, that will be featured at NY Fashion Week and Paris Fashion Week) and more surprises! Scan the QR code to learn more."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,88 +2,119 @@
|
|||||||
"eventsList": [
|
"eventsList": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"title":"什么?只要发布设计作品就有机会来香港与大佬面对面交流?!",
|
"title": "什么?只要发布设计作品就有机会来香港与大佬面对面交流?!",
|
||||||
"imgUrl": "/image/events/workshop-Cn.jpg"
|
"imgUrl": "/image/events/workshop-Cn.jpg"
|
||||||
},{
|
},
|
||||||
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"title":"AiDA X SFT AI时尚设计比赛2024",
|
"title": "AiDA X SFT AI时尚设计比赛2024",
|
||||||
"imgUrl": "/image/events/Fashion-Award-2024.png"
|
"imgUrl": "/image/events/Fashion-Award-2024.png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"eventsItem":[
|
"eventsItem": [
|
||||||
{
|
{
|
||||||
"id":1,
|
"id": 3,
|
||||||
"title":"什么?只要发布设计作品就有机会来香港与大佬面对面交流?!",
|
"title": "AiDA全球设计奖 2026",
|
||||||
"imgUrl": "/image/events/workshop-Cn.jpg",
|
"imgUrl": "/image/events/award-poster-zh.gif",
|
||||||
"textList":[
|
"textList": [
|
||||||
{
|
{
|
||||||
"paragraph":[
|
"paragraph": [
|
||||||
{
|
{
|
||||||
"text":"🎨这是一趟艺术巅峰之旅!AiDA Workshop!"
|
"text": "秉承推动 AI 赋能创意设计的初衷,Code‑Create 举办了「AiDA 全球设计大奖 2026」,面向来自香港、中国、新加坡、韩国及全球的设计师,鼓励大家探索 AI 与时尚设计的无限可能,突破传统界限,释放科技与想象力的创新潜能。点击“查看详情”按钮获取更多比赛信息,抓住成为 AI 时尚先锋的机会吧!"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},{
|
},
|
||||||
"paragraph":[
|
|
||||||
{
|
{
|
||||||
"text":"参与过程很简单,利用AiDA 在 “Gallery广场 ”发布设计作品,最终获赞最高者(至少20个赞)将被邀请至11月14日 举办的AiDA Workshop香港线下活动,与英国皇家艺术学院(RCA)、韩国知名时尚品牌BESFXXK创始人JAE以及优秀设计师一同交流!(名额仅限1名)"
|
"paragraph": [
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
{
|
||||||
"text":"<b>⚠️注意❗❗</b>"
|
"text": "参赛者将有机会赢取总奖金 9,000 美元,作品还将获得国际媒体展示机会,并与全球设计师和行业领袖建立联系。入围决赛者将受邀参加在香港举办的 专属颁奖典礼,主办方提供差旅支持,让设计师在国际舞台展示才华、拓展人脉,并共同庆祝创意成果。"
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
|
||||||
"text":"1. 作品描述添加tag: #AiDAworkshop_2024"
|
|
||||||
},{
|
|
||||||
"text":"2. 一个冠军名额"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
|
||||||
"text":"<b>🤩Code-Create将提供(适用条款条规):</b>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
|
||||||
"text":"✅往返机票/动车费用(仅限中国地区)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
|
||||||
"text":"✅一晚酒店住宿费用"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
"paragraph":[
|
|
||||||
{
|
|
||||||
"text":"⌛️截止时间:2024.10.31"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id":2,
|
"id": 1,
|
||||||
"title":"AiDA X SFT AI时尚设计比赛2024",
|
"title": "什么?只要发布设计作品就有机会来香港与大佬面对面交流?!",
|
||||||
"imgUrl": "/image/events/Fashion-Award-2024.png",
|
"imgUrl": "/image/events/workshop-Cn.jpg",
|
||||||
"textList":[
|
"textList": [
|
||||||
{
|
{
|
||||||
"paragraph":[
|
"paragraph": [
|
||||||
{
|
{
|
||||||
"text":"秉承着激发学生使用AI进行时尚设计的创新能力的初衷,Code-Create和香港理工大学时装及纺织学院(SFT)共同举办了“AiDA X SFT AI时尚设计比赛2024”让学生们在比赛中获得宝贵的AiDA实践经验,为未来的时尚设计行业打下了坚实的基础,成为时尚界的AI先锋。"
|
"text": "🎨这是一趟艺术巅峰之旅!AiDA Workshop!"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},{
|
},
|
||||||
"paragraph":[
|
|
||||||
{
|
{
|
||||||
"text":" 此次比赛面向全体SFT 学生,最终获奖者将赢取丰厚奖金(最高可达2万港币),获得在BESFXXK的实习机会(将与著名设计师Lim Jae Hyuk先生合作设计BESFXXK 系列,该系列将在纽约时装周和巴黎时装周上展出)及更多惊喜哦!扫描二维码获取更多比赛信息。"
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "参与过程很简单,利用AiDA 在 “Gallery广场 ”发布设计作品,最终获赞最高者(至少20个赞)将被邀请至11月14日 举办的AiDA Workshop香港线下活动,与英国皇家艺术学院(RCA)、韩国知名时尚品牌BESFXXK创始人JAE以及优秀设计师一同交流!(名额仅限1名)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "<b>⚠️注意❗❗</b>"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "1. 作品描述添加tag: #AiDAworkshop_2024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "2. 一个冠军名额"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "<b>🤩Code-Create将提供(适用条款条规):</b>"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "✅往返机票/动车费用(仅限中国地区)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "✅一晚酒店住宿费用"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "⌛️截止时间:2024.10.31"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"title": "AiDA X SFT AI时尚设计比赛2024",
|
||||||
|
"imgUrl": "/image/events/Fashion-Award-2024.png",
|
||||||
|
"textList": [
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": "秉承着激发学生使用AI进行时尚设计的创新能力的初衷,Code-Create和香港理工大学时装及纺织学院(SFT)共同举办了“AiDA X SFT AI时尚设计比赛2024”让学生们在比赛中获得宝贵的AiDA实践经验,为未来的时尚设计行业打下了坚实的基础,成为时尚界的AI先锋。"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"paragraph": [
|
||||||
|
{
|
||||||
|
"text": " 此次比赛面向全体SFT 学生,最终获奖者将赢取丰厚奖金(最高可达2万港币),获得在BESFXXK的实习机会(将与著名设计师Lim Jae Hyuk先生合作设计BESFXXK 系列,该系列将在纽约时装周和巴黎时装周上展出)及更多惊喜哦!扫描二维码获取更多比赛信息。"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2488,3 +2488,25 @@ textarea:focus {
|
|||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
color: #000;
|
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;
|
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;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="admin_page">
|
<div class="admin_page all-user">
|
||||||
<div class="admin_table_search">
|
<div class="admin_table_search">
|
||||||
<div class="admin_state">
|
<div class="admin_state">
|
||||||
<div class="admin_state_item">
|
<div class="admin_state_item">
|
||||||
@@ -18,7 +18,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="admin_state_item">
|
<div class="admin_state_item">
|
||||||
<span>{{ $t('admin.UserName') }}:</span>
|
<span>{{ $t('admin.UserName') }}:</span>
|
||||||
<SelectUser v-model:value="ids" multiple valueKey="label" labelKey="email" />
|
<!-- <a-select
|
||||||
|
v-model:value="ids"
|
||||||
|
mode="multiple"
|
||||||
|
style="width: 230px"
|
||||||
|
:field-names="{ label: 'label', value: 'label' }"
|
||||||
|
:filter-option="filterOption"
|
||||||
|
:placeholder="$t('admin.selectUserName')"
|
||||||
|
max-tag-count="responsive"
|
||||||
|
:options="allUserList"
|
||||||
|
@keydown.enter="gettrialList"
|
||||||
|
></a-select> -->
|
||||||
|
<SelectUser v-model="ids" labelKey="label" valueKey="label" multiple />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="admin_search">
|
<div class="admin_search">
|
||||||
@@ -81,18 +92,11 @@
|
|||||||
}"
|
}"
|
||||||
@click="plan.status !== 'PENDING' && selectPlanFilter(plan.id)"
|
@click="plan.status !== 'PENDING' && selectPlanFilter(plan.id)"
|
||||||
>
|
>
|
||||||
<a-tooltip v-if="plan.status === 'PENDING'">
|
|
||||||
<template #title>{{ $t('admin.PlanStart') }} {{ plan.startTime }}</template>
|
|
||||||
<span class="plan_name">{{ plan.name }}</span>
|
|
||||||
<MoreOutlined class="plan_more_icon" />
|
|
||||||
</a-tooltip>
|
|
||||||
<template v-else>
|
|
||||||
<span class="plan_name">{{ plan.name }}</span>
|
<span class="plan_name">{{ plan.name }}</span>
|
||||||
<MoreOutlined
|
<MoreOutlined
|
||||||
class="plan_more_icon"
|
class="plan_more_icon"
|
||||||
@click.stop="plan.status !== 'PENDING' && openPlanRenameModal(plan)"
|
@click.stop="plan.status !== 'PENDING' && openPlanRenameModal(plan)"
|
||||||
/>
|
/>
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-table
|
<a-table
|
||||||
@@ -159,9 +163,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
ref,
|
ref,
|
||||||
createVNode,
|
createVNode,
|
||||||
@@ -170,16 +174,16 @@ import {
|
|||||||
toRefs,
|
toRefs,
|
||||||
unref,
|
unref,
|
||||||
watch
|
watch
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { formatTime } from '@/tool/util'
|
import { formatTime } from '@/tool/util'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { Https } from '@/tool/https'
|
import { Https } from '@/tool/https'
|
||||||
import { Modal, message, Input } from 'ant-design-vue'
|
import { Modal, message, Input } from 'ant-design-vue'
|
||||||
import { ExclamationCircleOutlined, MoreOutlined } from '@ant-design/icons-vue'
|
import { ExclamationCircleOutlined, MoreOutlined } from '@ant-design/icons-vue'
|
||||||
import allUserPoerationsVue from './addAllUser.vue'
|
import allUserPoerationsVue from './addAllUser.vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import SelectUser from '@/component/common/SelectUser.vue'
|
import SelectUser from '@/component/common/SelectUser.vue'
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { allUserPoerationsVue, MoreOutlined, SelectUser },
|
components: { allUserPoerationsVue, MoreOutlined, SelectUser },
|
||||||
setup() {
|
setup() {
|
||||||
const store: any = useStore()
|
const store: any = useStore()
|
||||||
@@ -193,6 +197,9 @@ export default defineComponent({
|
|||||||
let filter: any = reactive({
|
let filter: any = reactive({
|
||||||
dataList: [],
|
dataList: [],
|
||||||
tableLoading: false,
|
tableLoading: false,
|
||||||
|
allUserList: computed(() => {
|
||||||
|
return store.state.adminPage.allUserList
|
||||||
|
}),
|
||||||
allCountry: [],
|
allCountry: [],
|
||||||
rowSelection: computed(() => {
|
rowSelection: computed(() => {
|
||||||
return {
|
return {
|
||||||
@@ -432,9 +439,6 @@ export default defineComponent({
|
|||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
status: ['ACTIVE', 'PENDING']
|
status: ['ACTIVE', 'PENDING']
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
res.forEach(plan => {
|
|
||||||
plan.startTime = formatTime(plan.currentPeriodStart, 'YYYY-MM-DD hh:mm:ss')
|
|
||||||
})
|
|
||||||
// 将与当前用户 subscriptionPlanId 相同的订阅计划放到第一个
|
// 将与当前用户 subscriptionPlanId 相同的订阅计划放到第一个
|
||||||
const userSubscriptionPlanId = store.state.UserHabit.userDetail.subscriptionPlanId
|
const userSubscriptionPlanId = store.state.UserHabit.userDetail.subscriptionPlanId
|
||||||
if (userSubscriptionPlanId && Array.isArray(res)) {
|
if (userSubscriptionPlanId && Array.isArray(res)) {
|
||||||
@@ -449,7 +453,6 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
planFilterOptions.value = res
|
planFilterOptions.value = res
|
||||||
}
|
}
|
||||||
console.log(planFilterOptions.value)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 监听组织ID,获取到值后再拉取订阅计划
|
// 监听组织ID,获取到值后再拉取订阅计划
|
||||||
@@ -664,27 +667,27 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.admin_page {
|
.admin_page {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.admin_table_content {
|
.admin_table_content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.admin_page .admin_table_search .admin_state {
|
.admin_page .admin_table_search .admin_state {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 70%;
|
width: 70%;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-content: flex-start;
|
align-content: flex-start;
|
||||||
}
|
}
|
||||||
.admin_page .admin_table_search .admin_search {
|
.admin_page .admin_table_search .admin_search {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
width: 30%;
|
width: 30%;
|
||||||
@@ -692,8 +695,8 @@ export default defineComponent({
|
|||||||
height: 4rem;
|
height: 4rem;
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.plan_list {
|
.plan_list {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
// flex-wrap: wrap;
|
// flex-wrap: wrap;
|
||||||
@@ -749,13 +752,13 @@ export default defineComponent({
|
|||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.subscription-plan-cell {
|
.subscription-plan-cell {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
.rename-plan-form {
|
.rename-plan-form {
|
||||||
padding: 2rem 0;
|
padding: 2rem 0;
|
||||||
.admin_state_item {
|
.admin_state_item {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -765,5 +768,13 @@ export default defineComponent({
|
|||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
.all-user {
|
||||||
|
.admin_table_content {
|
||||||
|
:deep(.ant-table-wrapper) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -148,6 +148,7 @@
|
|||||||
total: total,
|
total: total,
|
||||||
showQuickJumper: true,
|
showQuickJumper: true,
|
||||||
bordered: false,
|
bordered: false,
|
||||||
|
showTotal: (total) => `Total Transaction: ${total}`
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, text, record, index }">
|
<template #bodyCell="{ column, text, record, index }">
|
||||||
@@ -465,13 +466,16 @@ export default defineComponent({
|
|||||||
(rv: any) => {
|
(rv: any) => {
|
||||||
if (rv) {
|
if (rv) {
|
||||||
// this.dataList = rv
|
// this.dataList = rv
|
||||||
|
// console.log('rv----',rv);
|
||||||
|
|
||||||
filter.dataList = rv.content;
|
filter.dataList = rv.content;
|
||||||
filterData.total = rv.total;
|
filterData.total = rv.total;
|
||||||
filter.tableLoading = false;
|
filter.tableLoading = false;
|
||||||
filterData.totalPayer = rv.content.reduce((total: number, item: any) => {
|
filterData.totalPayer = rv.totalAmount;
|
||||||
const value = item && item.status === 'Success' ? parseFloat(item.payerTotal) : 0;
|
// filterData.totalPayer = rv.content.reduce((total: number, item: any) => {
|
||||||
return total + (isNaN(value) ? 0 : value);
|
// const value = item && item.status === 'Success' ? parseFloat(item.payerTotal) : 0;
|
||||||
}, 0);
|
// return total + (isNaN(value) ? 0 : value);
|
||||||
|
// }, 0);
|
||||||
|
|
||||||
// this.workspaceItem.position = this.singleTypeList[0].label
|
// 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.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.oldWidth = this.backgroundObject.width;
|
||||||
this.oldHeight = this.backgroundObject.height;
|
this.oldHeight = this.backgroundObject.height;
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ export class FillGroupLayerBackgroundCommand extends Command {
|
|||||||
);
|
);
|
||||||
this.layer = layer;
|
this.layer = layer;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
console.log("==========",layer);
|
|
||||||
if (!layer) return false;
|
if (!layer) return false;
|
||||||
|
|
||||||
if(!isUndo){
|
if(!isUndo){
|
||||||
@@ -107,7 +106,6 @@ export class FillGroupLayerBackgroundCommand extends Command {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断fabricObjects是否是组对象
|
// 判断fabricObjects是否是组对象
|
||||||
const firstObj = layer.fabricObjects?.[0] || null;
|
const firstObj = layer.fabricObjects?.[0] || null;
|
||||||
// 如果没有找到第一个对象,则直接添加到当前画布
|
// 如果没有找到第一个对象,则直接添加到当前画布
|
||||||
@@ -174,8 +172,8 @@ export class FillGroupLayerBackgroundCommand extends Command {
|
|||||||
}
|
}
|
||||||
const canvasObj = findObjectById(this.canvas, firstObj?.id)?.object;
|
const canvasObj = findObjectById(this.canvas, firstObj?.id)?.object;
|
||||||
if (
|
if (
|
||||||
(canvasObj && canvasObj.type === "group") ||
|
canvasObj && (canvasObj.type === "group" ||
|
||||||
canvasObj._objects?.length > 0
|
canvasObj._objects?.length > 0)
|
||||||
) {
|
) {
|
||||||
this.newFill.set({
|
this.newFill.set({
|
||||||
left: 0,
|
left: 0,
|
||||||
@@ -343,10 +341,6 @@ export class FillGroupLayerBackgroundCommand extends Command {
|
|||||||
minTop = Infinity,
|
minTop = Infinity,
|
||||||
maxRight = -Infinity,
|
maxRight = -Infinity,
|
||||||
maxBottom = -Infinity;
|
maxBottom = -Infinity;
|
||||||
console.log(
|
|
||||||
"计算当前所有对象的边界信息:===>",
|
|
||||||
this.originalfabricObjects.length
|
|
||||||
);
|
|
||||||
this.originalfabricObjects?.forEach((obj) => {
|
this.originalfabricObjects?.forEach((obj) => {
|
||||||
const { object } = findObjectById(this.canvas, obj.id) || {};
|
const { object } = findObjectById(this.canvas, obj.id) || {};
|
||||||
if (object) {
|
if (object) {
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import {
|
|||||||
insertObjectAtZIndex,
|
insertObjectAtZIndex,
|
||||||
removeCanvasObjectByObject,
|
removeCanvasObjectByObject,
|
||||||
createPatternTransform,
|
createPatternTransform,
|
||||||
|
getTransformScaleAngle,
|
||||||
|
imageAddGapToCanvas,
|
||||||
} from "../utils/helper";
|
} from "../utils/helper";
|
||||||
import { restoreFabricObject } from "../utils/objectHelper";
|
import { restoreFabricObject } from "../utils/objectHelper";
|
||||||
|
|
||||||
const scale = 0.3;// 默认缩放比例
|
|
||||||
|
|
||||||
export const FillSourceToBase64 = (source) => {
|
export const FillSourceToBase64 = (source) => {
|
||||||
if (source?.toDataURL) {
|
if (source?.toDataURL) {
|
||||||
return source.toDataURL?.();
|
return source.toDataURL?.();
|
||||||
@@ -38,7 +38,6 @@ export class FillRepeatCommand extends Command {
|
|||||||
this.fillRepeat = options.fillRepeat;
|
this.fillRepeat = options.fillRepeat;
|
||||||
this.oldObjects = null;
|
this.oldObjects = null;
|
||||||
this.oldLocked = null;
|
this.oldLocked = null;
|
||||||
this.oldIsDisableUnlock = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute() {
|
async execute() {
|
||||||
@@ -63,17 +62,15 @@ export class FillRepeatCommand extends Command {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
image.set({
|
image.set({
|
||||||
id: object.id,
|
...this.copyObjectProperties(object),
|
||||||
layerId: object.layerId,
|
|
||||||
layerName: object.layerName,
|
|
||||||
...(fill_.originalInfo || {
|
...(fill_.originalInfo || {
|
||||||
top: object.top,
|
top: object.top,
|
||||||
left: object.left,
|
left: object.left,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
layer.fabricObjects = [image.toObject(["id", "layerId", "layerName"])];
|
layer.fabricObjects = [image.toObject(["id", "layerId", "layerName"])];
|
||||||
this.oldLocked = layer.locked;
|
// this.oldLocked = layer.locked;
|
||||||
layer.locked = false;
|
// layer.locked = false;
|
||||||
|
|
||||||
this.canvas.add(image);
|
this.canvas.add(image);
|
||||||
this.canvas.remove(object);
|
this.canvas.remove(object);
|
||||||
@@ -109,24 +106,35 @@ export class FillRepeatCommand extends Command {
|
|||||||
height: object.height,
|
height: object.height,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const fdObject = this.canvasManager.getFixedLayerObject();
|
||||||
const bgObject = this.canvasManager.getBackgroundLayerObject();
|
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({
|
const pattern = new fabric.Pattern({
|
||||||
source: img,
|
source: img,
|
||||||
repeat: this.fillRepeat,
|
repeat: this.fillRepeat,
|
||||||
patternTransform: object.fill?.hasOwnProperty("patternTransform") ? object.fill.patternTransform : createPatternTransform(scale, 0),
|
patternTransform,
|
||||||
offsetX: object.fill?.hasOwnProperty("offsetX") ? object.fill.offsetX : bgObject.width / 2, // 水平偏移
|
offsetX, // 水平偏移
|
||||||
offsetY: object.fill?.hasOwnProperty("offsetY") ? object.fill.offsetY : bgObject.height / 2, // 垂直偏移
|
offsetY, // 垂直偏移
|
||||||
});
|
});
|
||||||
const rect = new fabric.Rect({
|
const rect = new fabric.Rect({
|
||||||
id: object.id,
|
...this.copyObjectProperties(object),
|
||||||
layerId: object.layerId,
|
|
||||||
layerName: object.layerName,
|
|
||||||
fill_,
|
fill_,
|
||||||
});
|
});
|
||||||
layer.fabricObjects = [rect.toObject(["id", "layerId", "layerName"])];
|
layer.fabricObjects = [rect.toObject(["id", "layerId", "layerName"])];
|
||||||
this.oldLocked = layer.locked;
|
// this.oldLocked = layer.locked;
|
||||||
// this.oldIsDisableUnlock = layer.isDisableUnlock;
|
|
||||||
// layer.isDisableUnlock = true;
|
|
||||||
if (this.oldObjects.type === "rect") {
|
if (this.oldObjects.type === "rect") {
|
||||||
rect.set({
|
rect.set({
|
||||||
width: object.width,
|
width: object.width,
|
||||||
@@ -142,17 +150,17 @@ export class FillRepeatCommand extends Command {
|
|||||||
flipY: object.flipY,
|
flipY: object.flipY,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let scaleX = bgObject.scaleX || 1;
|
let scaleX = tObject.scaleX || 1;
|
||||||
let scaleY = bgObject.scaleY || 1;
|
let scaleY = tObject.scaleY || 1;
|
||||||
rect.set({
|
rect.set({
|
||||||
width: bgObject.width,
|
width: tWidth,
|
||||||
height: bgObject.height,
|
height: tHeight,
|
||||||
top: bgObject.top - bgObject.height * scaleY / 2,
|
top: tObject.top - tHeight * scaleY / 2,
|
||||||
left: bgObject.left - bgObject.width * scaleX / 2,
|
left: tObject.left - tWidth * scaleX / 2,
|
||||||
scaleX,
|
scaleX,
|
||||||
scaleY,
|
scaleY,
|
||||||
});
|
});
|
||||||
layer.locked = true;
|
// layer.locked = true;
|
||||||
}
|
}
|
||||||
rect.set("fill", pattern);
|
rect.set("fill", pattern);
|
||||||
this.canvas.add(rect);
|
this.canvas.add(rect);
|
||||||
@@ -181,14 +189,23 @@ export class FillRepeatCommand extends Command {
|
|||||||
this.canvas.remove(object);
|
this.canvas.remove(object);
|
||||||
this.canvas.add(this.oldObjects);
|
this.canvas.add(this.oldObjects);
|
||||||
layer.fabricObjects = [this.oldObjects.toObject(["id", "layerId", "layerName"])];
|
layer.fabricObjects = [this.oldObjects.toObject(["id", "layerId", "layerName"])];
|
||||||
layer.locked = this.oldLocked;
|
// layer.locked = this.oldLocked;
|
||||||
// layer.isDisableUnlock = this.oldIsDisableUnlock;
|
|
||||||
await this.layerManager?.updateLayersObjectsInteractivity();
|
await this.layerManager?.updateLayersObjectsInteractivity();
|
||||||
await this.layerManager?.sortLayersWithTool?.();
|
await this.layerManager?.sortLayersWithTool?.();
|
||||||
this.canvas.renderAll();
|
this.canvas.renderAll();
|
||||||
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layerId);
|
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layerId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 复制原对象的属性
|
||||||
|
copyObjectProperties(object) {
|
||||||
|
return {
|
||||||
|
id: object.id,
|
||||||
|
layerId: object.layerId,
|
||||||
|
layerName: object.layerName,
|
||||||
|
isPrintTrims: object.isPrintTrims,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -227,6 +244,10 @@ export class FillRepeatChangeCommand extends Command {
|
|||||||
...this.newPattern,
|
...this.newPattern,
|
||||||
});
|
});
|
||||||
object.set("fill", pattern);
|
object.set("fill", pattern);
|
||||||
|
if (object.globalCompositeOperation_) {
|
||||||
|
object.globalCompositeOperation = object.globalCompositeOperation_;
|
||||||
|
object.globalCompositeOperation_ = null;
|
||||||
|
}
|
||||||
this.canvas.renderAll();
|
this.canvas.renderAll();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -273,7 +294,7 @@ export class FillRepeatGapChangeCommand extends Command {
|
|||||||
this.oldGapY = null;
|
this.oldGapY = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(isUndo = false) {
|
async execute(isCommand = true, isUndo = false) {
|
||||||
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
|
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
|
||||||
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
|
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
|
||||||
console.warn("图层不存在或没有 fabric 对象");
|
console.warn("图层不存在或没有 fabric 对象");
|
||||||
@@ -304,21 +325,18 @@ export class FillRepeatGapChangeCommand extends Command {
|
|||||||
object.fill_.gapY = this.newGapY;
|
object.fill_.gapY = this.newGapY;
|
||||||
}
|
}
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
|
image.crossOrigin = "anonymous";
|
||||||
image.src = object.fill_.source;
|
image.src = object.fill_.source;
|
||||||
await image.decode();
|
await image.decode();
|
||||||
object.fill_.width = image.width;
|
object.fill_.width = image.width;
|
||||||
object.fill_.height = image.height;
|
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");
|
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));
|
object.set("fill", new fabric.Pattern(fill));
|
||||||
|
if (isCommand && object.globalCompositeOperation_) {
|
||||||
|
object.globalCompositeOperation = object.globalCompositeOperation_;
|
||||||
|
object.globalCompositeOperation_ = null;
|
||||||
|
}
|
||||||
this.canvas.renderAll();
|
this.canvas.renderAll();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -328,7 +346,7 @@ export class FillRepeatGapChangeCommand extends Command {
|
|||||||
console.warn("没有旧间隙可恢复");
|
console.warn("没有旧间隙可恢复");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
await this.execute(true);
|
await this.execute(true, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -147,11 +147,11 @@ export class LassoCutoutCommand extends CompositeCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 确定源图层
|
// 确定源图层
|
||||||
const sourceLayer = this.layerManager.getActiveLayer();
|
// const sourceLayer = this.layerManager.getActiveLayer();
|
||||||
if (!sourceLayer) {
|
// if (!sourceLayer) {
|
||||||
console.error("无法执行套索抠图:源图层无效");
|
// console.error("无法执行套索抠图:源图层无效");
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 获取源图层的所有对象(包括子图层)
|
// 获取源图层的所有对象(包括子图层)
|
||||||
// const sourceObjects = this._getLayerObjects(sourceLayer);
|
// const sourceObjects = this._getLayerObjects(sourceLayer);
|
||||||
@@ -225,7 +225,7 @@ export class LassoCutoutCommand extends CompositeCommand {
|
|||||||
|
|
||||||
const layers = this.layerManager.layers.value;
|
const layers = this.layerManager.layers.value;
|
||||||
var topLayerIndex = 0;
|
var topLayerIndex = 0;
|
||||||
layers.forEach((layer, index) => {
|
if(this.originalLayer)layers.forEach((layer, index) => {
|
||||||
if (layer.id === this.originalLayer.id) {
|
if (layer.id === this.originalLayer.id) {
|
||||||
topLayerIndex = index;
|
topLayerIndex = index;
|
||||||
}else if (layer.children.length > 0) {
|
}else if (layer.children.length > 0) {
|
||||||
|
|||||||
@@ -280,8 +280,13 @@ export class PasteLayerCommand extends Command {
|
|||||||
isCut: undefined,
|
isCut: undefined,
|
||||||
serializedObjects: undefined,
|
serializedObjects: undefined,
|
||||||
};
|
};
|
||||||
|
if(this.newLayer.isPrintTrims){
|
||||||
if (this.insertIndex !== undefined && this.insertIndex !== null) {
|
this.layers.value.forEach((layer) => {
|
||||||
|
if (layer.isPrintTrimsGroup) {
|
||||||
|
layer.children.unshift(this.newLayer);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}else if (this.insertIndex !== undefined && this.insertIndex !== null) {
|
||||||
this.layers.value.splice(this.insertIndex, 0, this.newLayer);
|
this.layers.value.splice(this.insertIndex, 0, this.newLayer);
|
||||||
} else {
|
} else {
|
||||||
this.layers.value.push(this.newLayer);
|
this.layers.value.push(this.newLayer);
|
||||||
@@ -872,13 +877,13 @@ export class ToggleChildLayerVisibilityCommand extends Command {
|
|||||||
// this.oldVisibility = this.childLayer ? this.childLayer.visible : null;
|
// this.oldVisibility = this.childLayer ? this.childLayer.visible : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute() {
|
async execute(visible) {
|
||||||
if (!this.childLayer) {
|
if (!this.childLayer) {
|
||||||
throw new Error("找不到要切换可见性的子图层");
|
throw new Error("找不到要切换可见性的子图层");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换可见性
|
// 切换可见性
|
||||||
this.childLayer.visible = !this.childLayer.visible;
|
this.childLayer.visible = typeof visible === "boolean" ? visible : !this.childLayer.visible;
|
||||||
|
|
||||||
// 更新画布上图层对象的可见性
|
// 更新画布上图层对象的可见性
|
||||||
if (this.canvas) {
|
if (this.canvas) {
|
||||||
@@ -3684,7 +3689,7 @@ export class ChangeFixedImageCommand extends Command {
|
|||||||
opacity: currentObj.opacity,
|
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
|
false
|
||||||
);
|
);
|
||||||
if (insertSuccess) {
|
if (insertSuccess) {
|
||||||
console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
|
// console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
|
||||||
} else {
|
} else {
|
||||||
// 如果插入失败,回退到普通添加
|
// 如果插入失败,回退到普通添加
|
||||||
this.canvas.add(newImage);
|
this.canvas.add(newImage);
|
||||||
|
|||||||
@@ -600,7 +600,7 @@ export class ChangeFixedImageCommand extends Command {
|
|||||||
opacity: currentObj.opacity,
|
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
|
false
|
||||||
);
|
);
|
||||||
if (insertSuccess) {
|
if (insertSuccess) {
|
||||||
console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
|
// console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
|
||||||
} else {
|
} else {
|
||||||
// 如果插入失败,回退到普通添加
|
// 如果插入失败,回退到普通添加
|
||||||
this.canvas.add(newImage);
|
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);
|
this.objectsToRasterize = objectsWithZIndex.map((item) => item.object);
|
||||||
|
|
||||||
console.log(
|
// console.log(`📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合` );
|
||||||
`📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合`
|
// console.log(
|
||||||
);
|
// "🔢 对象z-index顺序:",
|
||||||
console.log(
|
// objectsWithZIndex.map((item) => ({
|
||||||
"🔢 对象z-index顺序:",
|
// id: item.object.id,
|
||||||
objectsWithZIndex.map((item) => ({
|
// type: item.object.type,
|
||||||
id: item.object.id,
|
// zIndex: item.zIndex,
|
||||||
type: item.object.type,
|
// }))
|
||||||
zIndex: item.zIndex,
|
// );
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -611,17 +609,17 @@ export class ExportLayerToImageCommand extends Command {
|
|||||||
// 提取排序后的对象
|
// 提取排序后的对象
|
||||||
this.objectsToRasterize = objectsWithZIndex.map((item) => item.object);
|
this.objectsToRasterize = objectsWithZIndex.map((item) => item.object);
|
||||||
|
|
||||||
console.log(
|
// console.log(
|
||||||
`📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合`
|
// `📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合`
|
||||||
);
|
// );
|
||||||
console.log(
|
// console.log(
|
||||||
"🔢 对象z-index顺序:",
|
// "🔢 对象z-index顺序:",
|
||||||
objectsWithZIndex.map((item) => ({
|
// objectsWithZIndex.map((item) => ({
|
||||||
id: item.object.id,
|
// id: item.object.id,
|
||||||
type: item.object.type,
|
// type: item.object.type,
|
||||||
zIndex: item.zIndex,
|
// zIndex: item.zIndex,
|
||||||
}))
|
// }))
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -159,6 +159,8 @@ export class BatchInitializeRedGreenModeCommand extends Command {
|
|||||||
absolutePositioned: true,
|
absolutePositioned: true,
|
||||||
opacity: 0.01, // 设置为几乎透明
|
opacity: 0.01, // 设置为几乎透明
|
||||||
id: generateId("redGreenImageMask_"),
|
id: generateId("redGreenImageMask_"),
|
||||||
|
rx: 15,
|
||||||
|
ry: 15,
|
||||||
});
|
});
|
||||||
// this.canvas.add(this.redGreenImageMask);
|
// this.canvas.add(this.redGreenImageMask);
|
||||||
this.canvas.clipPath = this.redGreenImageMask;
|
this.canvas.clipPath = this.redGreenImageMask;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class TransformCommand extends Command {
|
|||||||
this.layerManager = options.layerManager;
|
this.layerManager = options.layerManager;
|
||||||
this.layers = options.layers || null;
|
this.layers = options.layers || null;
|
||||||
this.lastSelectLayerId = options.lastSelectLayerId || null; // 最后选择的图层ID
|
this.lastSelectLayerId = options.lastSelectLayerId || null; // 最后选择的图层ID
|
||||||
|
this.isCommand = options.isCommand == undefined ? true : options.isCommand
|
||||||
const targetObject =
|
const targetObject =
|
||||||
findObjectById(this.canvas, this.objectId)?.object || null;
|
findObjectById(this.canvas, this.objectId)?.object || null;
|
||||||
|
|
||||||
@@ -189,6 +189,11 @@ export class TransformCommand extends Command {
|
|||||||
object.set(key, value);
|
object.set(key, value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(this.isCommand && object.globalCompositeOperation_){
|
||||||
|
object.globalCompositeOperation = object.globalCompositeOperation_;
|
||||||
|
object.globalCompositeOperation_ = null;
|
||||||
|
}
|
||||||
|
|
||||||
// 确保对象更新
|
// 确保对象更新
|
||||||
object.setCoords();
|
object.setCoords();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ const isVisible = computed(() => {
|
|||||||
OperationType.ERASER,
|
OperationType.ERASER,
|
||||||
OperationType.RED_BRUSH,
|
OperationType.RED_BRUSH,
|
||||||
OperationType.GREEN_BRUSH,
|
OperationType.GREEN_BRUSH,
|
||||||
|
OperationType.PART_BRUSH,
|
||||||
|
OperationType.PART_ERASER,
|
||||||
].includes(props.activeTool);
|
].includes(props.activeTool);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const layerManager = inject("layerManager");
|
|||||||
const isShowLayerPanel = inject("isShowLayerPanel", ref(false));
|
const isShowLayerPanel = inject("isShowLayerPanel", ref(false));
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
title: String,
|
||||||
activeTool: String,
|
activeTool: String,
|
||||||
canvasWidth: Number,
|
canvasWidth: Number,
|
||||||
canvasHeight: Number,
|
canvasHeight: Number,
|
||||||
@@ -22,6 +23,7 @@ const props = defineProps({
|
|||||||
default: true, // 是否显示图层面板
|
default: true, // 是否显示图层面板
|
||||||
},
|
},
|
||||||
isBackgroundChangeable: Boolean,
|
isBackgroundChangeable: Boolean,
|
||||||
|
isChangeCanvasSize: Boolean,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
@@ -273,7 +275,7 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<div class="canvas-header">
|
<div class="canvas-header">
|
||||||
<div class="canvas-header-wrapper">
|
<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="
|
<!-- v-if="
|
||||||
!activeTool ||
|
!activeTool ||
|
||||||
@@ -284,6 +286,7 @@ onMounted(() => {
|
|||||||
<div class="setting-group">
|
<div class="setting-group">
|
||||||
<span class="setting-label">{{ $t("Canvas.width") }}</span>
|
<span class="setting-label">{{ $t("Canvas.width") }}</span>
|
||||||
<a-input-number
|
<a-input-number
|
||||||
|
:disabled="!isChangeCanvasSize"
|
||||||
:value="canvasWidth?.toFixed(2)"
|
:value="canvasWidth?.toFixed(2)"
|
||||||
class="setting-input"
|
class="setting-input"
|
||||||
:min="1"
|
:min="1"
|
||||||
@@ -300,6 +303,7 @@ onMounted(() => {
|
|||||||
<div class="setting-group">
|
<div class="setting-group">
|
||||||
<span class="setting-label">{{ $t("Canvas.height") }}</span>
|
<span class="setting-label">{{ $t("Canvas.height") }}</span>
|
||||||
<a-input-number
|
<a-input-number
|
||||||
|
:disabled="!isChangeCanvasSize"
|
||||||
:value="canvasHeight?.toFixed(2)"
|
:value="canvasHeight?.toFixed(2)"
|
||||||
class="setting-input"
|
class="setting-input"
|
||||||
:min="1"
|
:min="1"
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ const canDeleteComputed = computed(() => {
|
|||||||
return parentLayer?.children?.length > 1;
|
return parentLayer?.children?.length > 1;
|
||||||
}
|
}
|
||||||
// 否则直接返回根图层的可删除状态
|
// 否则直接返回根图层的可删除状态
|
||||||
return props.layers.length > 3;
|
return true;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -287,7 +287,7 @@ const canDeleteComputed = computed(() => {
|
|||||||
:is-child="isChild"
|
:is-child="isChild"
|
||||||
:is-active="layer.id === activeLayerId"
|
:is-active="layer.id === activeLayerId"
|
||||||
:is-selected="isLayerSelected(layer.id)"
|
:is-selected="isLayerSelected(layer.id)"
|
||||||
:is-multi-select-mode="isMultiSelectMode && !layer.specialType"
|
:is-multi-select-mode="isMultiSelectMode && !(layer.isPrintTrims || layer.isPrintTrimsGroup)"
|
||||||
:is-editing="editingLayerId === layer.id"
|
:is-editing="editingLayerId === layer.id"
|
||||||
:editing-name="editingLayerName"
|
:editing-name="editingLayerName"
|
||||||
:can-delete="
|
:can-delete="
|
||||||
@@ -296,7 +296,7 @@ const canDeleteComputed = computed(() => {
|
|||||||
:expanded-group-ids="expandedGroupIds"
|
:expanded-group-ids="expandedGroupIds"
|
||||||
@click="(...args) => forwardEvent('layer-click', ...args)"
|
@click="(...args) => forwardEvent('layer-click', ...args)"
|
||||||
@double-click="(...args) => forwardEvent('layer-double-click', ...args)"
|
@double-click="(...args) => forwardEvent('layer-double-click', ...args)"
|
||||||
@context-menu="(...args) => !layer.specialType && forwardEvent('context-menu', ...args)"
|
@context-menu="(...args) => !(layer.isPrintTrims || layer.isPrintTrimsGroup) && forwardEvent('context-menu', ...args)"
|
||||||
@checkbox-change="(...args) => forwardEvent('checkbox-change', ...args)"
|
@checkbox-change="(...args) => forwardEvent('checkbox-change', ...args)"
|
||||||
@toggle-visibility="(...args) => forwardEvent('toggle-visibility', ...args)"
|
@toggle-visibility="(...args) => forwardEvent('toggle-visibility', ...args)"
|
||||||
@toggle-lock="(...args) => forwardEvent('toggle-lock', ...args)"
|
@toggle-lock="(...args) => forwardEvent('toggle-lock', ...args)"
|
||||||
@@ -337,7 +337,7 @@ const canDeleteComputed = computed(() => {
|
|||||||
:expanded-group-ids="expandedGroupIds"
|
:expanded-group-ids="expandedGroupIds"
|
||||||
:isChild="true"
|
:isChild="true"
|
||||||
:parentLayerId="layer.id"
|
:parentLayerId="layer.id"
|
||||||
:group-name="layer.specialType || groupName"
|
:group-name="groupName"
|
||||||
@layer-click="(...args) => forwardEvent('layer-click', ...args)"
|
@layer-click="(...args) => forwardEvent('layer-click', ...args)"
|
||||||
@layer-double-click="(...args) => forwardEvent('layer-double-click', ...args)"
|
@layer-double-click="(...args) => forwardEvent('layer-double-click', ...args)"
|
||||||
@context-menu="(...args) => forwardEvent('context-menu', ...args)"
|
@context-menu="(...args) => forwardEvent('context-menu', ...args)"
|
||||||
|
|||||||
@@ -414,14 +414,14 @@ function deleteSelectedLayers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查删除后是否还有足够的普通图层
|
// 检查删除后是否还有足够的普通图层
|
||||||
const remainingNormalLayers = layers.value.filter(
|
// const remainingNormalLayers = layers.value.filter(
|
||||||
(layer) => !layer.isBackground && !layer.isFixed && !selectedLayerIds.value.includes(layer.id)
|
// (layer) => !layer.isBackground && !layer.isFixed && !selectedLayerIds.value.includes(layer.id)
|
||||||
).length;
|
// ).length;
|
||||||
|
|
||||||
if (remainingNormalLayers < 1) {
|
// if (remainingNormalLayers < 1) {
|
||||||
console.warn("不能删除所有普通图层");
|
// console.warn("不能删除所有普通图层");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 确认删除
|
// 确认删除
|
||||||
if (selectedLayers.length > 1) {
|
if (selectedLayers.length > 1) {
|
||||||
@@ -584,15 +584,16 @@ function handleLayerClick(layer, event) {
|
|||||||
// 如果不是多选模式,才可激活图层
|
// 如果不是多选模式,才可激活图层
|
||||||
// 1.如果是组,则设置组下的第一个子图层为活动图层
|
// 1.如果是组,则设置组下的第一个子图层为活动图层
|
||||||
// 2.否则直接设置活动图层
|
// 2.否则直接设置活动图层
|
||||||
if (isGroupLayerType(layer) && layer.children && layer.children.length > 0) {
|
if (isGroupLayerType(layer) && layer.children && layer.children.length > 0 && !layer.isPrintTrimsGroup) {
|
||||||
// 如果是组图层,设置第一个子图层为活动图层
|
// 如果是组图层,设置第一个子图层为活动图层
|
||||||
layerManager?.setAllActiveGroupLayerCanvasObject?.(layer);
|
layerManager?.setAllActiveGroupLayerCanvasObject?.(layer);
|
||||||
setActiveLayer(layer.children[0].id, { parentId: layer.id });
|
setActiveLayer(layer.children[0].id, { parentId: layer.id });
|
||||||
} else {
|
} 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();
|
layerManager?.updateLayersObjectsInteractivity();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -876,13 +877,15 @@ function toggleSelectedLayersVisibility() {
|
|||||||
|
|
||||||
function canDeleteLayers() {
|
function canDeleteLayers() {
|
||||||
const selectedLayers = getSelectedLayers();
|
const selectedLayers = getSelectedLayers();
|
||||||
|
console.log(selectedLayers);
|
||||||
if (selectedLayers.length === 0) return false;
|
if (selectedLayers.length === 0) return false;
|
||||||
|
|
||||||
// 检查是否包含不能删除的图层
|
// 检查是否包含不能删除的图层
|
||||||
const undeletableLayers = selectedLayers.filter((layer) => layer.isBackground || layer.isFixed);
|
const undeletableLayers = selectedLayers.filter((layer) => layer.isBackground || layer.isFixed);
|
||||||
|
|
||||||
if (undeletableLayers.length > 0) return false;
|
if (undeletableLayers.length > 0) return false;
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
// 检查删除后是否还有足够的普通图层
|
// 检查删除后是否还有足够的普通图层
|
||||||
const remainingNormalLayers = layers.value.filter(
|
const remainingNormalLayers = layers.value.filter(
|
||||||
(layer) => !layer.isBackground && !layer.isFixed && !selectedLayerIds.value.includes(layer.id)
|
(layer) => !layer.isBackground && !layer.isFixed && !selectedLayerIds.value.includes(layer.id)
|
||||||
@@ -1242,7 +1245,7 @@ async function handleCrossLevelMove(moveData) {
|
|||||||
try {
|
try {
|
||||||
const layer = findLayerRecursively(layers.value, layerId).layer;
|
const layer = findLayerRecursively(layers.value, layerId).layer;
|
||||||
const toLayer = findLayerRecursively(layers.value, toParentId).layer;
|
const toLayer = findLayerRecursively(layers.value, toParentId).layer;
|
||||||
if(layer?.specialType || toLayer?.specialType) {
|
if(layer?.isPrintTrims || layer?.isPrintTrimsGroup || toLayer?.isPrintTrims || toLayer?.isPrintTrimsGroup) {
|
||||||
console.warn("当前图层不可移动到外部");
|
console.warn("当前图层不可移动到外部");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
const palletRef = ref(null)
|
const palletRef = ref(null)
|
||||||
watch(()=>palletData.color_,(newVal:any)=>{
|
watch(()=>palletData.color_,(newVal:any)=>{
|
||||||
if(!newVal?.rgba?.r)return
|
if(newVal?.rgba?.r == null)return
|
||||||
if(palletData.color?.gradient?.gradientShow){
|
if(palletData.color?.gradient?.gradientShow){
|
||||||
palletData.color.gradient.gradientList[palletData.color.gradient.selectIndex].rgba = {
|
palletData.color.gradient.gradientList[palletData.color.gradient.selectIndex].rgba = {
|
||||||
r:newVal.rgba.r,
|
r:newVal.rgba.r,
|
||||||
@@ -143,7 +143,7 @@ export default defineComponent({
|
|||||||
},{deep: true })
|
},{deep: true })
|
||||||
const setOperate = ()=>{
|
const setOperate = ()=>{
|
||||||
if(!palletData.color.rgba)return message.info(t('DesignDetailAlter.jsContent7'))
|
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.selectIndex = 0
|
||||||
palletData.gradient.gradientShow = true
|
palletData.gradient.gradientShow = true
|
||||||
if(!palletData.color.gradient){
|
if(!palletData.color.gradient){
|
||||||
@@ -257,7 +257,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
}
|
}
|
||||||
const openPallet = ()=>{
|
const openPallet = ()=>{
|
||||||
if(palletData.palletShow && props.selectColor?.rgba?.r){
|
if(palletData.palletShow && props.selectColor?.rgba?.r != null){
|
||||||
if(props.selectColor.gradient){
|
if(props.selectColor.gradient){
|
||||||
palletData.color_.rgba = props.selectColor.gradient.gradientList[0].rgba
|
palletData.color_.rgba = props.selectColor.gradient.gradientList[0].rgba
|
||||||
}else{
|
}else{
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<span class="label">{{ t("Canvas.scale") }}</span>
|
<span class="label">{{ t("Canvas.scale") }}</span>
|
||||||
<slider
|
<slider
|
||||||
:min="1"
|
:min="1"
|
||||||
:max="500"
|
:max="1000"
|
||||||
:step="1"
|
:step="1"
|
||||||
is-input
|
is-input
|
||||||
:tipFormatter="(v) => `${scale}%`"
|
:tipFormatter="(v) => `${scale}%`"
|
||||||
@@ -32,8 +32,8 @@
|
|||||||
is-input
|
is-input
|
||||||
:tipFormatter="(v) => `${v}px`"
|
:tipFormatter="(v) => `${v}px`"
|
||||||
:value="gapX"
|
:value="gapX"
|
||||||
@input="(e) => emit('inputFill_Gap', e, gapY)"
|
@input="(e) => emit('inputFillGap', e, gapY)"
|
||||||
@change="(e) => emit('changeFill_Gap', e, gapY)"
|
@change="(e) => emit('changeFillGap', e, gapY)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="repeat-setting-item">
|
<div class="repeat-setting-item">
|
||||||
@@ -45,26 +45,26 @@
|
|||||||
is-input
|
is-input
|
||||||
:tipFormatter="(v) => `${v}px`"
|
:tipFormatter="(v) => `${v}px`"
|
||||||
:value="gapY"
|
:value="gapY"
|
||||||
@input="(e) => emit('inputFill_Gap', gapX, e)"
|
@input="(e) => emit('inputFillGap', gapX, e)"
|
||||||
@change="(e) => emit('changeFill_Gap', gapX, e)"
|
@change="(e) => emit('changeFillGap', gapX, e)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="repeat-setting-item">
|
<div class="repeat-setting-item">
|
||||||
<span class="label">{{ t("Canvas.offset") }}</span>
|
<span class="label">{{ t("Canvas.offset") }}</span>
|
||||||
<offset-tool
|
<offset-tool
|
||||||
:left="offsetX"
|
:left="offset.x"
|
||||||
:top="offsetY"
|
:top="offset.y"
|
||||||
@input="(e) => emit('inputFillOffset', e)"
|
@input="inputFillOffset"
|
||||||
@change="(e) => emit('changeFillOffset', e)"
|
@change="changeFillOffset"
|
||||||
:show-dish="false"
|
:show-dish="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="repeat-setting-item offset">
|
<div class="repeat-setting-item offset">
|
||||||
<offset-tool
|
<offset-tool
|
||||||
:left="offsetX"
|
:left="offset.x"
|
||||||
:top="offsetY"
|
:top="offset.y"
|
||||||
@input="(e) => emit('inputFillOffset', e)"
|
@input="inputFillOffset"
|
||||||
@change="(e) => emit('changeFillOffset', e)"
|
@change="changeFillOffset"
|
||||||
:show-input="false"
|
:show-input="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,6 +79,16 @@
|
|||||||
import Slider from "../tools/Slider.vue";
|
import Slider from "../tools/Slider.vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const emit = defineEmits([
|
||||||
|
"inputFillAngle",
|
||||||
|
"changeFillAngle",
|
||||||
|
"inputFillOffset",
|
||||||
|
"changeFillOffset",
|
||||||
|
"inputFillScale",
|
||||||
|
"changeFillScale",
|
||||||
|
"inputFillGap",
|
||||||
|
"changeFillGap",
|
||||||
|
]);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
object: {
|
object: {
|
||||||
@@ -89,36 +99,53 @@
|
|||||||
const angle = computed(
|
const angle = computed(
|
||||||
() => getTransformScaleAngle(props.object.fill?.patternTransform).angle
|
() => 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 gapX = computed(() => props.object.fill_?.gapX || 0);
|
||||||
const gapY = computed(() => props.object.fill_?.gapY || 0);
|
const gapY = computed(() => props.object.fill_?.gapY || 0);
|
||||||
const offsetX = computed(
|
|
||||||
() => (props.object.fill?.offsetX / props.object.width) * 100
|
// 缩放比例
|
||||||
);
|
const scale = computed(() => {
|
||||||
const offsetY = computed(
|
const object = props.object;
|
||||||
() => (props.object.fill?.offsetY / props.object.height) * 100
|
const patternTransform = object.fill?.patternTransform;
|
||||||
);
|
const scaleValue = getTransformScaleAngle(patternTransform).scale;
|
||||||
const emit = defineEmits([
|
const scaleX = scaleValue / (object.width / object.fill_.width / 5);
|
||||||
"inputFillAngle",
|
const scaleY = scaleValue / (object.height / object.fill_.height / 5);
|
||||||
"changeFillAngle",
|
const scaleXY = object.width > object.height ? scaleX : scaleY;
|
||||||
"inputFillOffset",
|
return Number(Number(scaleXY * 100).toFixed(2));
|
||||||
"changeFillOffset",
|
});
|
||||||
"inputFillScale",
|
const inputFillScale = (e) => setFillScale(e, true);
|
||||||
"changeFillScale",
|
const changeFillScale = (e) => setFillScale(e, false);
|
||||||
"inputFill_Gap",
|
const setFillScale = (e, isInput) => {
|
||||||
"changeFill_Gap",
|
const object = props.object;
|
||||||
]);
|
|
||||||
const inputFillScale = (e) => {
|
|
||||||
const scale = e / 100;
|
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>
|
</script>
|
||||||
|
|
||||||
@@ -126,6 +153,7 @@
|
|||||||
.repeat-setting {
|
.repeat-setting {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
width: 228px;
|
width: 228px;
|
||||||
|
overflow: hidden;
|
||||||
> .title {
|
> .title {
|
||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
v-for="v in activeObjects"
|
v-for="v in activeObjects"
|
||||||
:key="v.id"
|
:key="v.id"
|
||||||
>
|
>
|
||||||
<div class="title">{{ v.layer?.name }}</div>
|
<!-- <div class="title">{{ v.layer?.name }}</div> -->
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div
|
<div
|
||||||
class="input"
|
class="input"
|
||||||
@@ -125,6 +125,10 @@
|
|||||||
"
|
"
|
||||||
:options="selectOptions"
|
:options="selectOptions"
|
||||||
@change="(e) => changeFillRepeat(e, v)"
|
@change="(e) => changeFillRepeat(e, v)"
|
||||||
|
:disabled="
|
||||||
|
v.layer?.metadata?.sourceData?.type ===
|
||||||
|
'trims'
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- 平铺设置 -->
|
<!-- 平铺设置 -->
|
||||||
@@ -154,11 +158,11 @@
|
|||||||
@changeFillScale="
|
@changeFillScale="
|
||||||
(e) => changeFillScale(e, v)
|
(e) => changeFillScale(e, v)
|
||||||
"
|
"
|
||||||
@inputFill_Gap="
|
@inputFillGap="
|
||||||
(x, y) => inputFill_Gap(x, y, v)
|
(x, y) => inputFillGap(x, y, v)
|
||||||
"
|
"
|
||||||
@changeFill_Gap="
|
@changeFillGap="
|
||||||
(x, y) => changeFill_Gap(x, y, v)
|
(x, y) => changeFillGap(x, y, v)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@@ -178,11 +182,7 @@
|
|||||||
import { ref, onMounted, watch, onUnmounted, reactive } from "vue";
|
import { ref, onMounted, watch, onUnmounted, reactive } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
import {
|
import { OperationType, SpecialLayerId } from "../../utils/layerHelper";
|
||||||
OperationType,
|
|
||||||
SpecialLayerId,
|
|
||||||
SpecialType,
|
|
||||||
} from "../../utils/layerHelper";
|
|
||||||
import { loadImageUrlToLayer } from "../../utils/imageHelper";
|
import { loadImageUrlToLayer } from "../../utils/imageHelper";
|
||||||
import {
|
import {
|
||||||
calculateRotatedTopLeftDeg,
|
calculateRotatedTopLeftDeg,
|
||||||
@@ -283,16 +283,13 @@
|
|||||||
const getActiveObject = (e) => {
|
const getActiveObject = (e) => {
|
||||||
console.log("==========切换激活对象", e, activeObjects);
|
console.log("==========切换激活对象", e, activeObjects);
|
||||||
activeObjects.value = [...e.selected];
|
activeObjects.value = [...e.selected];
|
||||||
// .filter((v) =>
|
|
||||||
// v.specialType ? v.specialType === SpecialType.REPEAT_O : true
|
|
||||||
// );// 过滤出印花对象
|
|
||||||
activeObjects.value.forEach((v) => {
|
activeObjects.value.forEach((v) => {
|
||||||
v.layer = props.layerManager.getLayerById(v.layerId);
|
v.layer = props.layerManager.getLayerById(v.layerId);
|
||||||
});
|
});
|
||||||
if (activeObjects.value.length === 0) {
|
if (activeObjects.value.length === 1) {
|
||||||
close();
|
|
||||||
} else {
|
|
||||||
show();
|
show();
|
||||||
|
} else {
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//取消当前选中
|
//取消当前选中
|
||||||
@@ -318,6 +315,7 @@
|
|||||||
layerManager: props.layerManager,
|
layerManager: props.layerManager,
|
||||||
layers: layers,
|
layers: layers,
|
||||||
lastSelectLayerId: lastSelectLayerId,
|
lastSelectLayerId: lastSelectLayerId,
|
||||||
|
isCommand,
|
||||||
});
|
});
|
||||||
if (isCommand) {
|
if (isCommand) {
|
||||||
props.commandManager.execute(cmd);
|
props.commandManager.execute(cmd);
|
||||||
@@ -339,6 +337,7 @@
|
|||||||
const finalState = computeAngleState(angle, obj, initialState);
|
const finalState = computeAngleState(angle, obj, initialState);
|
||||||
transformObject(obj, initialState, finalState, false);
|
transformObject(obj, initialState, finalState, false);
|
||||||
if (!obj.hasOwnProperty("oldState")) obj.oldState = initialState;
|
if (!obj.hasOwnProperty("oldState")) obj.oldState = initialState;
|
||||||
|
props.canvasManager.beforeChangeCanvas([obj]);
|
||||||
};
|
};
|
||||||
const changeAngle = (angle, obj) => {
|
const changeAngle = (angle, obj) => {
|
||||||
var initialState;
|
var initialState;
|
||||||
@@ -431,6 +430,7 @@
|
|||||||
});
|
});
|
||||||
obj.set("fill", pattern);
|
obj.set("fill", pattern);
|
||||||
props.canvas.renderAll();
|
props.canvas.renderAll();
|
||||||
|
props.canvasManager.beforeChangeCanvas([obj]);
|
||||||
};
|
};
|
||||||
const changeFillAngle = (angle, obj) => {
|
const changeFillAngle = (angle, obj) => {
|
||||||
const fill = obj.get("fill");
|
const fill = obj.get("fill");
|
||||||
@@ -440,21 +440,22 @@
|
|||||||
};
|
};
|
||||||
changeFill(obj, pattern);
|
changeFill(obj, pattern);
|
||||||
};
|
};
|
||||||
// 改变填充便宜
|
// 改变填充偏移
|
||||||
const inputFillOffset = (value, obj) => {
|
const inputFillOffset = (value, obj) => {
|
||||||
if (!obj.oldPattern) obj.oldPattern = obj.get("fill");
|
if (!obj.oldPattern) obj.oldPattern = obj.get("fill");
|
||||||
const pattern = new fabric.Pattern({
|
const pattern = new fabric.Pattern({
|
||||||
...obj.get("fill"),
|
...obj.get("fill"),
|
||||||
offsetX: (value.left / 100) * obj.width,
|
offsetX: value.x,
|
||||||
offsetY: (value.top / 100) * obj.height,
|
offsetY: value.y,
|
||||||
});
|
});
|
||||||
obj.set("fill", pattern);
|
obj.set("fill", pattern);
|
||||||
props.canvas.renderAll();
|
props.canvas.renderAll();
|
||||||
|
props.canvasManager.beforeChangeCanvas([obj]);
|
||||||
};
|
};
|
||||||
const changeFillOffset = (value, obj) => {
|
const changeFillOffset = (value, obj) => {
|
||||||
const pattern = new fabric.Pattern({
|
const pattern = new fabric.Pattern({
|
||||||
offsetX: (value.left / 100) * obj.width,
|
offsetX: value.x,
|
||||||
offsetY: (value.top / 100) * obj.height,
|
offsetY: value.y,
|
||||||
});
|
});
|
||||||
changeFill(obj, pattern);
|
changeFill(obj, pattern);
|
||||||
};
|
};
|
||||||
@@ -469,6 +470,7 @@
|
|||||||
});
|
});
|
||||||
obj.set("fill", pattern);
|
obj.set("fill", pattern);
|
||||||
props.canvas.renderAll();
|
props.canvas.renderAll();
|
||||||
|
props.canvasManager.beforeChangeCanvas([obj]);
|
||||||
};
|
};
|
||||||
const changeFillScale = (scale, obj) => {
|
const changeFillScale = (scale, obj) => {
|
||||||
const fill = obj.get("fill");
|
const fill = obj.get("fill");
|
||||||
@@ -490,7 +492,7 @@
|
|||||||
props.commandManager.execute(cmd);
|
props.commandManager.execute(cmd);
|
||||||
};
|
};
|
||||||
// 改变填充间隙
|
// 改变填充间隙
|
||||||
const inputFill_Gap = (gapX, gapY, obj) => {
|
const inputFillGap = (gapX, gapY, obj) => {
|
||||||
const cmd = new FillRepeatGapChangeCommand({
|
const cmd = new FillRepeatGapChangeCommand({
|
||||||
canvas: props.canvas,
|
canvas: props.canvas,
|
||||||
layers: layers,
|
layers: layers,
|
||||||
@@ -501,9 +503,10 @@
|
|||||||
newGapY: gapY,
|
newGapY: gapY,
|
||||||
record: true,
|
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_) {
|
if (obj.oldFill_) {
|
||||||
obj.fill_ = { ...obj.oldFill_ };
|
obj.fill_ = { ...obj.oldFill_ };
|
||||||
delete obj.oldFill_;
|
delete obj.oldFill_;
|
||||||
@@ -767,7 +770,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tool-content {
|
.tool-content {
|
||||||
overflow-y: auto;
|
// overflow-y: auto;
|
||||||
max-height: 20rem;
|
max-height: 20rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
padding: 0 1.5rem;
|
padding: 0 1.5rem;
|
||||||
|
|||||||
@@ -29,8 +29,11 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
clothingMinIOPath: {
|
||||||
|
type: String,
|
||||||
|
default: "", // 衣服底图URL-线稿
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const commandManager = inject("commandManager");
|
const commandManager = inject("commandManager");
|
||||||
const layerManager = inject("layerManager"); // 图层管理器
|
const layerManager = inject("layerManager"); // 图层管理器
|
||||||
|
|
||||||
@@ -249,7 +252,13 @@ const redGreenToolsList = ref([
|
|||||||
|
|
||||||
// 根据模式选择工具列表
|
// 根据模式选择工具列表
|
||||||
const toolsList = computed(() => {
|
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) {
|
function selectTool(tool, isRedGreenMode = false) {
|
||||||
|
|||||||
@@ -9,11 +9,9 @@ import {
|
|||||||
isGroupLayer,
|
isGroupLayer,
|
||||||
OperationType,
|
OperationType,
|
||||||
OperationTypes,
|
OperationTypes,
|
||||||
findLayer,
|
|
||||||
createLayer,
|
createLayer,
|
||||||
LayerType,
|
LayerType,
|
||||||
SpecialLayerId,
|
SpecialLayerId,
|
||||||
SpecialType,
|
|
||||||
BlendMode,
|
BlendMode,
|
||||||
} from "../utils/layerHelper";
|
} from "../utils/layerHelper";
|
||||||
import { ObjectMoveCommand } from "../commands/ObjectCommands";
|
import { ObjectMoveCommand } from "../commands/ObjectCommands";
|
||||||
@@ -21,7 +19,6 @@ import { AnimationManager } from "./animation/AnimationManager";
|
|||||||
import { createCanvas } from "../utils/canvasFactory";
|
import { createCanvas } from "../utils/canvasFactory";
|
||||||
import { CanvasEventManager } from "./events/CanvasEventManager";
|
import { CanvasEventManager } from "./events/CanvasEventManager";
|
||||||
import CanvasConfig from "../config/canvasConfig";
|
import CanvasConfig from "../config/canvasConfig";
|
||||||
import { RedGreenModeManager } from "./RedGreenModeManager";
|
|
||||||
import { EraserStateManager } from "./EraserStateManager";
|
import { EraserStateManager } from "./EraserStateManager";
|
||||||
import {
|
import {
|
||||||
deepClone,
|
deepClone,
|
||||||
@@ -32,9 +29,11 @@ import {
|
|||||||
fillToCssStyle,
|
fillToCssStyle,
|
||||||
calculateRotatedTopLeftDeg,
|
calculateRotatedTopLeftDeg,
|
||||||
calculateCenterPoint,
|
calculateCenterPoint,
|
||||||
|
calculateTopLeftPoint,
|
||||||
createPatternTransform,
|
createPatternTransform,
|
||||||
getTransformScaleAngle,
|
getTransformScaleAngle,
|
||||||
base64ToCanvas,
|
base64ToCanvas,
|
||||||
|
imageAddGapToCanvas,
|
||||||
} from "../utils/helper";
|
} from "../utils/helper";
|
||||||
import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands";
|
import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands";
|
||||||
import { isFunction } from "lodash-es";
|
import { isFunction } from "lodash-es";
|
||||||
@@ -45,7 +44,7 @@ import {
|
|||||||
} from "../utils/layerUtils";
|
} from "../utils/layerUtils";
|
||||||
import { imageModeHandler } from "../utils/imageHelper";
|
import { imageModeHandler } from "../utils/imageHelper";
|
||||||
import { getObjectAlphaToCanvas } from "../utils/objectHelper";
|
import { getObjectAlphaToCanvas } from "../utils/objectHelper";
|
||||||
import { AddLayerCommand, RemoveLayerCommand } from "../commands/LayerCommands";
|
import { AddLayerCommand, RemoveLayerCommand, ToggleChildLayerVisibilityCommand } from "../commands/LayerCommands";
|
||||||
import { fa, id } from "element-plus/es/locales.mjs";
|
import { fa, id } from "element-plus/es/locales.mjs";
|
||||||
import i18n from "@/lang/index.ts";
|
import i18n from "@/lang/index.ts";
|
||||||
const {t} = i18n.global;
|
const {t} = i18n.global;
|
||||||
@@ -69,8 +68,11 @@ export class CanvasManager {
|
|||||||
this.isFixedErasable = options.isFixedErasable || false; // 是否允许擦除固定图层
|
this.isFixedErasable = options.isFixedErasable || false; // 是否允许擦除固定图层
|
||||||
this.eraserStateManager = null; // 橡皮擦状态管理器引用
|
this.eraserStateManager = null; // 橡皮擦状态管理器引用
|
||||||
this.handleCanvasInit = null; // 画布初始化回调函数
|
this.handleCanvasInit = null; // 画布初始化回调函数
|
||||||
|
this.partManager = options.partManager || null;
|
||||||
this.props = options.props || {};
|
this.props = options.props || {};
|
||||||
this.emit = options.emit || (() => {});
|
this.emit = options.emit || (() => {});
|
||||||
|
this.awaitCanvasRun = null;
|
||||||
|
this.canvasChangeing = false;
|
||||||
// 初始化画布
|
// 初始化画布
|
||||||
this.initializeCanvas();
|
this.initializeCanvas();
|
||||||
}
|
}
|
||||||
@@ -173,7 +175,12 @@ export class CanvasManager {
|
|||||||
_initCanvasEvents() {
|
_initCanvasEvents() {
|
||||||
// 添加笔刷图像转换处理回调
|
// 添加笔刷图像转换处理回调
|
||||||
this.canvas.onBrushImageConverted = async (fabricImage) => {
|
this.canvas.onBrushImageConverted = async (fabricImage) => {
|
||||||
|
const activeTool = this.toolManager?.activeTool?.value;
|
||||||
|
if(activeTool === OperationType.PART_BRUSH){
|
||||||
|
this.partManager?.addDrawPartImage(fabricImage);
|
||||||
|
}else{
|
||||||
await this.addImageToLayer({ fabricImage, targetLayerId: null });
|
await this.addImageToLayer({ fabricImage, targetLayerId: null });
|
||||||
|
}
|
||||||
// 返回false表示使用默认行为(直接添加到画布)
|
// 返回false表示使用默认行为(直接添加到画布)
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -195,6 +202,10 @@ export class CanvasManager {
|
|||||||
console.log("擦除完成", e.targets);
|
console.log("擦除完成", e.targets);
|
||||||
// 可以在这里保存状态到命令管理器
|
// 可以在这里保存状态到命令管理器
|
||||||
const affectedObjects = 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);
|
const command = this.eraserStateManager.endErasing(affectedObjects);
|
||||||
if (command && this.commandManager) {
|
if (command && this.commandManager) {
|
||||||
await this.commandManager?.executeCommand?.(command);
|
await this.commandManager?.executeCommand?.(command);
|
||||||
@@ -305,6 +316,19 @@ export class CanvasManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置部件选择管理器
|
||||||
|
* @param {Object} partManager 部件选择管理器实例
|
||||||
|
*/
|
||||||
|
setPartManager(partManager) {
|
||||||
|
this.partManager = partManager;
|
||||||
|
|
||||||
|
// 如果已创建事件管理器,更新它的部件选择管理器引用
|
||||||
|
if (this.eventManager) {
|
||||||
|
this.eventManager.partManager = this.partManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 设置红绿图模式管理器
|
// 设置红绿图模式管理器
|
||||||
setRedGreenModeManager(redGreenModeManager) {
|
setRedGreenModeManager(redGreenModeManager) {
|
||||||
this.redGreenModeManager = redGreenModeManager;
|
this.redGreenModeManager = redGreenModeManager;
|
||||||
@@ -313,6 +337,7 @@ export class CanvasManager {
|
|||||||
setupCanvasEvents(activeElementId, layerManager) {
|
setupCanvasEvents(activeElementId, layerManager) {
|
||||||
// 创建画布事件管理器
|
// 创建画布事件管理器
|
||||||
this.eventManager = new CanvasEventManager(this.canvas, {
|
this.eventManager = new CanvasEventManager(this.canvas, {
|
||||||
|
canvasManager: this,
|
||||||
toolManager: this.toolManager,
|
toolManager: this.toolManager,
|
||||||
animationManager: this.animationManager,
|
animationManager: this.animationManager,
|
||||||
thumbnailManager: this.thumbnailManager,
|
thumbnailManager: this.thumbnailManager,
|
||||||
@@ -430,12 +455,41 @@ export class CanvasManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 居中所有画布元素,包括背景层和其他元素
|
// 居中所有画布元素,包括背景层和其他元素
|
||||||
this.centerAllObjects();
|
await this.centerAllObjects();
|
||||||
|
|
||||||
// // 重新渲染画布使变更生效
|
// // 重新渲染画布使变更生效
|
||||||
// this.canvas.renderAll();
|
// 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
|
* @private
|
||||||
@@ -455,7 +509,6 @@ export class CanvasManager {
|
|||||||
*/
|
*/
|
||||||
async centerAllObjects() {
|
async centerAllObjects() {
|
||||||
if (!this.canvas) return;
|
if (!this.canvas) return;
|
||||||
|
|
||||||
// 获取所有可见对象(不是背景元素的对象)
|
// 获取所有可见对象(不是背景元素的对象)
|
||||||
const allObjects = this.canvas.getObjects();
|
const allObjects = this.canvas.getObjects();
|
||||||
if (allObjects.length === 0) return;
|
if (allObjects.length === 0) return;
|
||||||
@@ -470,9 +523,6 @@ export class CanvasManager {
|
|||||||
// 获取背景对象
|
// 获取背景对象
|
||||||
const backgroundObject = visibleObjects.find((obj) => obj.isBackground);
|
const backgroundObject = visibleObjects.find((obj) => obj.isBackground);
|
||||||
|
|
||||||
// !this.canvas?.clipPath &&
|
|
||||||
// this.centerBackgroundLayer(this.canvas.width, this.canvas.height);
|
|
||||||
|
|
||||||
this.canvas?.clipPath?.set?.({
|
this.canvas?.clipPath?.set?.({
|
||||||
left: this.width / 2,
|
left: this.width / 2,
|
||||||
top: this.height / 2,
|
top: this.height / 2,
|
||||||
@@ -565,6 +615,9 @@ export class CanvasManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
!this.canvas?.clipPath &&
|
||||||
|
this.centerBackgroundLayer(this.canvas.width, this.canvas.height);
|
||||||
|
|
||||||
// 如果有背景层,更新蒙层位置
|
// 如果有背景层,更新蒙层位置
|
||||||
if (backgroundObject && CanvasConfig.isCropBackground) {
|
if (backgroundObject && CanvasConfig.isCropBackground) {
|
||||||
this.updateMaskPosition(backgroundObject);
|
this.updateMaskPosition(backgroundObject);
|
||||||
@@ -717,6 +770,8 @@ export class CanvasManager {
|
|||||||
originX: backgroundLayerObject.originX || "left",
|
originX: backgroundLayerObject.originX || "left",
|
||||||
originY: backgroundLayerObject.originY || "top",
|
originY: backgroundLayerObject.originY || "top",
|
||||||
absolutePositioned: true,
|
absolutePositioned: true,
|
||||||
|
rx: 15,
|
||||||
|
ry: 15,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getBackgroundLayer() {
|
getBackgroundLayer() {
|
||||||
@@ -813,9 +868,9 @@ export class CanvasManager {
|
|||||||
|
|
||||||
return layerObjectByLayerId;
|
return layerObjectByLayerId;
|
||||||
}
|
}
|
||||||
getObjectsByIds(ids){
|
getObjectsByIdOrLayerId(ids){
|
||||||
const objects = this.canvas.getObjects().filter((obj) => {
|
const objects = this.canvas.getObjects().filter((obj) => {
|
||||||
return ids.includes(obj.id);
|
return ids.includes(obj.id) || ids.includes(obj.layerId);
|
||||||
});
|
});
|
||||||
return objects;
|
return objects;
|
||||||
}
|
}
|
||||||
@@ -827,7 +882,6 @@ export class CanvasManager {
|
|||||||
updateMaskPosition(backgroundLayerObject) {
|
updateMaskPosition(backgroundLayerObject) {
|
||||||
if (!backgroundLayerObject || !this.maskLayer || !this.canvas.clipPath)
|
if (!backgroundLayerObject || !this.maskLayer || !this.canvas.clipPath)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const left = backgroundLayerObject.left;
|
const left = backgroundLayerObject.left;
|
||||||
const top = backgroundLayerObject.top;
|
const top = backgroundLayerObject.top;
|
||||||
|
|
||||||
@@ -922,6 +976,9 @@ export class CanvasManager {
|
|||||||
* @param {Boolean} options.isContainBg 是否包含背景图层
|
* @param {Boolean} options.isContainBg 是否包含背景图层
|
||||||
* @param {Boolean} options.isContainFixed 是否包含固定图层
|
* @param {Boolean} options.isContainFixed 是否包含固定图层
|
||||||
* @param {Boolean} options.isContainFixedOther 是否包含其他固定图层
|
* @param {Boolean} options.isContainFixedOther 是否包含其他固定图层
|
||||||
|
* @param {Boolean} options.isPrintTrimsNoRepeat 是否包含印花图层的不平铺
|
||||||
|
* @param {Boolean} options.isPrintTrimsRepeat 是否包含印花图层的平铺
|
||||||
|
* @param {Boolean} options.isContainNormalLayer 是否包含普通图层
|
||||||
* @param {String} options.layerId 导出具体图层ID
|
* @param {String} options.layerId 导出具体图层ID
|
||||||
* @param {Array} options.layerIdArray 导出多个图层ID数组
|
* @param {Array} options.layerIdArray 导出多个图层ID数组
|
||||||
* @param {String} options.expPicType 导出图片类型 (png/jpg/svg)
|
* @param {String} options.expPicType 导出图片类型 (png/jpg/svg)
|
||||||
@@ -942,6 +999,9 @@ export class CanvasManager {
|
|||||||
// this.canvas.renderAll(); // 重新渲染画布
|
// this.canvas.renderAll(); // 重新渲染画布
|
||||||
// 自动设置红绿图模式相关参数
|
// 自动设置红绿图模式相关参数
|
||||||
const enhancedOptions = {
|
const enhancedOptions = {
|
||||||
|
isPrintTrimsNoRepeat: true,
|
||||||
|
isPrintTrimsRepeat: true,
|
||||||
|
isContainNormalLayer: true,
|
||||||
...options,
|
...options,
|
||||||
// 如果没有明确指定,则根据当前模式自动设置
|
// 如果没有明确指定,则根据当前模式自动设置
|
||||||
restoreOpacityInRedGreen:
|
restoreOpacityInRedGreen:
|
||||||
@@ -972,7 +1032,46 @@ export class CanvasManager {
|
|||||||
console.log("红绿图模式导出图层:", normalLayerIds);
|
console.log("红绿图模式导出图层:", normalLayerIds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await this.exportManager.exportImage(enhancedOptions);
|
|
||||||
|
// 处理特殊图层的显示状态
|
||||||
|
const ptlids = [];
|
||||||
|
if(!enhancedOptions.isPrintTrimsNoRepeat || !enhancedOptions.isPrintTrimsRepeat){
|
||||||
|
let layers = this.layers?.value?.find((layer) => layer.isPrintTrimsGroup)?.children || [];
|
||||||
|
for(let layer of layers){
|
||||||
|
if(!layer.visible) continue;
|
||||||
|
let repeat = layer.fabricObjects?.[0]?.fill?.repeat || "no-repeat";
|
||||||
|
if(typeof repeat !== "string") repeat = "no-repeat";
|
||||||
|
if(repeat === "no-repeat"){
|
||||||
|
if(enhancedOptions.isPrintTrimsNoRepeat) continue;
|
||||||
|
}else{
|
||||||
|
if(enhancedOptions.isPrintTrimsRepeat) continue;
|
||||||
|
}
|
||||||
|
ptlids.push(layer.id);
|
||||||
|
const command = new ToggleChildLayerVisibilityCommand({
|
||||||
|
canvas: this.canvas,
|
||||||
|
layers: this.layers,
|
||||||
|
layerId: layer.id,
|
||||||
|
layerManager: this.layerManager,
|
||||||
|
});
|
||||||
|
await command.execute(false);
|
||||||
|
}
|
||||||
|
await this.changeCanvas();
|
||||||
|
}
|
||||||
|
const res = await this.exportManager.exportImage(enhancedOptions);
|
||||||
|
// 恢复特殊图层的显示状态
|
||||||
|
if(ptlids.length > 0){
|
||||||
|
for(let id of ptlids){
|
||||||
|
const command = new ToggleChildLayerVisibilityCommand({
|
||||||
|
canvas: this.canvas,
|
||||||
|
layers: this.layers,
|
||||||
|
layerId: id,
|
||||||
|
layerManager: this.layerManager,
|
||||||
|
});
|
||||||
|
await command.execute(true);
|
||||||
|
}
|
||||||
|
await this.changeCanvas();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("CanvasManager导出图片失败:", error);
|
console.warn("CanvasManager导出图片失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -989,10 +1088,12 @@ export class CanvasManager {
|
|||||||
// 导出印花和元素图层信息
|
// 导出印花和元素图层信息
|
||||||
const printTrimsData = await this.exportPrintTrimsLayers().catch(() => ({prints: null, trims: null}));
|
const printTrimsData = await this.exportPrintTrimsLayers().catch(() => ({prints: null, trims: null}));
|
||||||
|
|
||||||
return {
|
const obj = {
|
||||||
color,
|
color,
|
||||||
...printTrimsData,
|
...printTrimsData,
|
||||||
};
|
};
|
||||||
|
console.log("==========exportExtraInfo:", obj);
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1046,7 +1147,7 @@ export class CanvasManager {
|
|||||||
const glayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
|
const glayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
|
||||||
if(!glayer) return Promise.reject("印花和元素图层组不存在");
|
if(!glayer) return Promise.reject("印花和元素图层组不存在");
|
||||||
const ids = glayer.children.map((v) => v.id);
|
const ids = glayer.children.map((v) => v.id);
|
||||||
const objects = this.getObjectsByIds(ids);
|
const objects = this.getObjectsByIdOrLayerId(ids);
|
||||||
const fixedLayerObj = this.getFixedLayerObject();
|
const fixedLayerObj = this.getFixedLayerObject();
|
||||||
if(!fixedLayerObj) return Promise.reject("固定图层不存在");
|
if(!fixedLayerObj) return Promise.reject("固定图层不存在");
|
||||||
const flWidth = fixedLayerObj.width
|
const flWidth = fixedLayerObj.width
|
||||||
@@ -1057,8 +1158,9 @@ export class CanvasManager {
|
|||||||
const flScaleY = fixedLayerObj.scaleY
|
const flScaleY = fixedLayerObj.scaleY
|
||||||
const prints = [];
|
const prints = [];
|
||||||
const trims = [];
|
const trims = [];
|
||||||
objects.forEach((v) => {
|
objects.forEach((v, i) => {
|
||||||
const sourceData = glayer.children.find((v_) => v_.id === v.id)?.metadata?.sourceData;
|
const label = glayer.children.find((v_) => (v_.id === v.layerId || v_.id === v.id));
|
||||||
|
const sourceData = label?.metadata?.sourceData;
|
||||||
if(!sourceData) return;
|
if(!sourceData) return;
|
||||||
const obj = {
|
const obj = {
|
||||||
ifSingle: typeof v.fill === "string",
|
ifSingle: typeof v.fill === "string",
|
||||||
@@ -1070,7 +1172,7 @@ export class CanvasManager {
|
|||||||
scale: [0, 0],
|
scale: [0, 0],
|
||||||
angle: v.angle,
|
angle: v.angle,
|
||||||
name: sourceData.name,
|
name: sourceData.name,
|
||||||
priority: sourceData.priority,
|
priority: i + 1,
|
||||||
object:{
|
object:{
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
@@ -1078,29 +1180,36 @@ export class CanvasManager {
|
|||||||
scaleY: 0,//对象的缩放比例
|
scaleY: 0,//对象的缩放比例
|
||||||
opacity: v.opacity,
|
opacity: v.opacity,
|
||||||
angle: v.angle,
|
angle: v.angle,
|
||||||
flipX: v.flipX,//是否水平翻转
|
flipX: v.flipX,
|
||||||
flipY: v.flipY,//是否垂直翻转
|
flipY: v.flipY,
|
||||||
blendMode: v.globalCompositeOperation,// 混合模式
|
blendMode: v.globalCompositeOperation,
|
||||||
gapX: 0,// 平铺模式下的间距
|
gapX: 0,// 平铺模式下的间距
|
||||||
gapY: 0,// 平铺模式下的间距
|
gapY: 0,// 平铺模式下的间距
|
||||||
|
fill_repeat: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let left = (v.left - (flLeft - flWidth * flScaleX / 2));
|
let left = (v.left - (flLeft - flWidth * flScaleX / 2));
|
||||||
let top = (v.top - (flTop - flHeight * flScaleY / 2));
|
let top = (v.top - (flTop - flHeight * flScaleY / 2));
|
||||||
let width = (v.width * v.scaleX);
|
let width = (v.width * v.scaleX);
|
||||||
let height = (v.height * v.scaleY);
|
let height = (v.height * v.scaleY);
|
||||||
let {x:cx, y:cy} = calculateCenterPoint(width, height, left, top, v.angle);
|
if(v.originX === "center" && v.originY === "center") {
|
||||||
let oX = (cx-width/2) / flScaleX;
|
let {x:cx, y:cy} = calculateTopLeftPoint(width, height, left, top, v.angle);
|
||||||
let oY = (cy-height/2) / flScaleY;
|
left = cx;
|
||||||
|
top = cy;
|
||||||
|
}
|
||||||
|
let oX = left / flScaleX;
|
||||||
|
let oY = top / flScaleY;
|
||||||
let oScaleX = (v.width * v.scaleX) / (flWidth * flScaleX);
|
let oScaleX = (v.width * v.scaleX) / (flWidth * flScaleX);
|
||||||
let oScaleY = (v.height * v.scaleY) / (flHeight * flScaleY);
|
let oScaleY = (v.height * v.scaleY) / (flHeight * flScaleY);
|
||||||
// obj.object.width = width;
|
|
||||||
// obj.object.height = height;
|
|
||||||
obj.object.top = oY;
|
obj.object.top = oY;
|
||||||
obj.object.left = oX;
|
obj.object.left = oX;
|
||||||
obj.object.scaleX = oScaleX;
|
obj.object.scaleX = oScaleX;
|
||||||
obj.object.scaleY = oScaleY;
|
obj.object.scaleY = oScaleY;
|
||||||
if(obj.ifSingle){
|
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.location = [oX, oY];
|
||||||
obj.scale = [oScaleX, oScaleY];
|
obj.scale = [oScaleX, oScaleY];
|
||||||
}else{
|
}else{
|
||||||
@@ -1118,17 +1227,19 @@ export class CanvasManager {
|
|||||||
obj.scale = [scaleXY, scaleXY];
|
obj.scale = [scaleXY, scaleXY];
|
||||||
obj.angle = angle;
|
obj.angle = angle;
|
||||||
obj.location = [left, top];
|
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);
|
prints.push(obj);
|
||||||
}else if(obj.level2Type === "Embroidery"){
|
}else if(sourceData.type === "trims"){
|
||||||
trims.push(obj);
|
trims.push(obj);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// prints.sort((a, b) => a.ifSingle ? 1 : -1);
|
// prints.sort((a, b) => a.ifSingle ? 1 : -1);
|
||||||
prints.forEach((v, i) => v.priority = i + 1);
|
// prints.forEach((v, i) => v.priority = i + 1);
|
||||||
trims.forEach((v, i) => v.priority = i + 1);
|
// trims.forEach((v, i) => v.priority = i + 1);
|
||||||
return {prints, trims};
|
return {prints, trims};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1215,6 +1326,7 @@ export class CanvasManager {
|
|||||||
canvasColor: this.canvasColor.value,
|
canvasColor: this.canvasColor.value,
|
||||||
activeLayerId: this.layerManager?.activeLayerId?.value,
|
activeLayerId: this.layerManager?.activeLayerId?.value,
|
||||||
};
|
};
|
||||||
|
this.FixJsonIdLoss(data);
|
||||||
console.log("获取画布JSON数据...", data);
|
console.log("获取画布JSON数据...", data);
|
||||||
return JSON.stringify(data);
|
return JSON.stringify(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1222,10 +1334,8 @@ export class CanvasManager {
|
|||||||
throw new Error("获取画布JSON失败");
|
throw new Error("获取画布JSON失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadJSON(json, calllBack) {
|
loadJSON(json, calllBack) {
|
||||||
console.log("加载画布JSON数据:", json);
|
this.canvas.loading.value = true;
|
||||||
|
|
||||||
// 确保传入的json是字符串格式
|
// 确保传入的json是字符串格式
|
||||||
if (typeof json === "object") {
|
if (typeof json === "object") {
|
||||||
json = JSON.stringify(json);
|
json = JSON.stringify(json);
|
||||||
@@ -1234,7 +1344,9 @@ export class CanvasManager {
|
|||||||
}
|
}
|
||||||
// 解析JSON字符串
|
// 解析JSON字符串
|
||||||
try {
|
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.canvasWidth.value = parsedJson.canvasWidth || this.width;
|
||||||
this.canvasHeight.value = parsedJson.canvasHeight || this.height;
|
this.canvasHeight.value = parsedJson.canvasHeight || this.height;
|
||||||
this.canvasColor.value = parsedJson.canvasColor || this.backgroundColor;
|
this.canvasColor.value = parsedJson.canvasColor || this.backgroundColor;
|
||||||
@@ -1260,7 +1372,7 @@ export class CanvasManager {
|
|||||||
// this.canvasHeight.value = parsedJson.canvasHeight || this.height;
|
// this.canvasHeight.value = parsedJson.canvasHeight || this.height;
|
||||||
// this.canvasColor.value = parsedJson.canvasColor || this.backgroundColor;
|
// this.canvasColor.value = parsedJson.canvasColor || this.backgroundColor;
|
||||||
|
|
||||||
console.log("是否检测到红绿图模式内容:", this.enabledRedGreenMode);
|
// console.log("是否检测到红绿图模式内容:", this.enabledRedGreenMode);
|
||||||
|
|
||||||
// 重置视图变换以确保元素位置正确
|
// 重置视图变换以确保元素位置正确
|
||||||
this._resetViewportTransform(1);
|
this._resetViewportTransform(1);
|
||||||
@@ -1272,7 +1384,7 @@ export class CanvasManager {
|
|||||||
|
|
||||||
// 清除当前画布内容
|
// 清除当前画布内容
|
||||||
// this.canvas.clear(); // 清除画布内容 可以先去掉 这样加载闪动的情况就比较少 如果有问题 可以再打开
|
// this.canvas.clear(); // 清除画布内容 可以先去掉 这样加载闪动的情况就比较少 如果有问题 可以再打开
|
||||||
console.log("清除当前画布内容", canvasData);
|
// console.log("清除当前画布内容", canvasData);
|
||||||
delete canvasData.clipPath; // 删除当前裁剪路径
|
delete canvasData.clipPath; // 删除当前裁剪路径
|
||||||
// 加载画布数据
|
// 加载画布数据
|
||||||
this.canvas.loadFromJSON(canvasData, async () => {
|
this.canvas.loadFromJSON(canvasData, async () => {
|
||||||
@@ -1301,7 +1413,7 @@ export class CanvasManager {
|
|||||||
// 重置画布数据
|
// 重置画布数据
|
||||||
await this.setCanvasSize(this.canvas.width, this.canvas.height);
|
await this.setCanvasSize(this.canvas.width, this.canvas.height);
|
||||||
await this.centerBackgroundLayer(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());
|
// restoreObjectLayerAssociations(this.layers.value, this.canvas.getObjects());
|
||||||
// 验证图层关联关系 - 稳定后可以注释
|
// 验证图层关联关系 - 稳定后可以注释
|
||||||
@@ -1313,7 +1425,7 @@ export class CanvasManager {
|
|||||||
// console.log("图层关联验证结果:", isValidate);
|
// console.log("图层关联验证结果:", isValidate);
|
||||||
// 排序
|
// 排序
|
||||||
// 使用LayerSort工具重新排列画布对象(如果可用)
|
// 使用LayerSort工具重新排列画布对象(如果可用)
|
||||||
await this?.layerManager?.layerSort?.rearrangeObjects();
|
await this?.layerManager?.layerSort?.rearrangeObjectsAsync();
|
||||||
|
|
||||||
this.layerManager.activeLayerId.value = this.layers.value[0]
|
this.layerManager.activeLayerId.value = this.layers.value[0]
|
||||||
.children?.length
|
.children?.length
|
||||||
@@ -1331,7 +1443,6 @@ export class CanvasManager {
|
|||||||
await this.layerManager?.updateLayersObjectsInteractivity?.();
|
await this.layerManager?.updateLayersObjectsInteractivity?.();
|
||||||
|
|
||||||
await calllBack?.();
|
await calllBack?.();
|
||||||
this.emit("canvas-load-json-success");
|
|
||||||
// 更新所有缩略图
|
// 更新所有缩略图
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.updateAllThumbnails();
|
this.updateAllThumbnails();
|
||||||
@@ -1353,27 +1464,56 @@ export class CanvasManager {
|
|||||||
throw new Error("解析JSON失败,请检查输入格式: " + error.message);
|
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 - 其他图层数据
|
* @param {Object} otherData - 其他图层数据
|
||||||
*/
|
*/
|
||||||
async createOtherLayers(otherData, isUpdate = false) {
|
async createOtherLayers(otherData) {
|
||||||
if (!otherData) return console.warn("otherData 为空不需要添加");
|
if (!otherData) return console.warn("otherData 为空不需要添加");
|
||||||
|
let resolve = ()=>{};
|
||||||
|
this.awaitCanvasRun = ()=>(new Promise((v) => resolve = v))
|
||||||
const otherData_ = JSON.parse(JSON.stringify(otherData));
|
const otherData_ = JSON.parse(JSON.stringify(otherData));
|
||||||
console.log("==========创建其他图层", otherData_);
|
console.log("==========创建其他图层", otherData_);
|
||||||
|
|
||||||
const updateColor = !!otherData_.color;
|
|
||||||
const updateSpecialGroup = !!otherData_.printObject || !!otherData_.trims;
|
|
||||||
// 删除颜色图层和特殊组图层
|
// 删除颜色图层和特殊组图层
|
||||||
const ids = [];
|
const ids = [SpecialLayerId.COLOR, SpecialLayerId.SPECIAL_GROUP];
|
||||||
if(isUpdate){
|
|
||||||
updateColor && ids.push(SpecialLayerId.COLOR)
|
|
||||||
updateSpecialGroup && ids.push(SpecialLayerId.SPECIAL_GROUP)
|
|
||||||
}else{
|
|
||||||
ids.push(SpecialLayerId.COLOR)
|
|
||||||
ids.push(SpecialLayerId.SPECIAL_GROUP)
|
|
||||||
}
|
|
||||||
this.layers.value = this.layers.value.filter((layer) => {
|
this.layers.value = this.layers.value.filter((layer) => {
|
||||||
if(ids.includes(layer.id)){
|
if(ids.includes(layer.id)){
|
||||||
ids.push(...layer.children?.map((child) => child.id));
|
ids.push(...layer.children?.map((child) => child.id));
|
||||||
@@ -1381,30 +1521,39 @@ export class CanvasManager {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
this.canvas.getObjects().forEach((v) => ids.includes(v.id) && this.canvas.remove(v))
|
this.canvas.getObjects().forEach((v) => {
|
||||||
|
if(ids.includes(v.id) || ids.includes(v.layerId)){
|
||||||
|
this.canvas.remove(v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
// 创建颜色图层
|
// 创建颜色图层
|
||||||
otherData_.color && await this.createColorLayer(otherData_.color);
|
await this.createColorLayer(otherData_.color);
|
||||||
|
|
||||||
const printTrimsLayers = [];// 印花和元素图层
|
const printTrimsLayers = [];// 印花和元素图层
|
||||||
const singleLayers = [];// 平铺图层
|
const singleLayers = [];// 平铺图层
|
||||||
otherData_.printObject?.prints?.forEach((print, index) => {
|
otherData_.printObject?.prints?.forEach((print, index) => {// 印花
|
||||||
print.name = t("Canvas.Print") + (index + 1);
|
print.name = t("Canvas.Print") + (index + 1);
|
||||||
|
print.type = "print";
|
||||||
if(print.ifSingle){
|
if(print.ifSingle){
|
||||||
printTrimsLayers.unshift({...print});
|
printTrimsLayers.unshift({...print});
|
||||||
}else{
|
}else{
|
||||||
singleLayers.unshift({...print});
|
singleLayers.unshift({...print});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
otherData_.trims?.prints?.forEach((trims, index) => {
|
otherData_.trims?.prints?.forEach((trims, index) => {// 元素
|
||||||
trims.name = t("Canvas.Elements") + (index + 1);
|
trims.name = t("Canvas.Elements") + (index + 1);
|
||||||
|
trims.type = "trims";
|
||||||
printTrimsLayers.unshift({...trims});
|
printTrimsLayers.unshift({...trims});
|
||||||
})
|
})
|
||||||
if(isUpdate ? updateSpecialGroup : true){
|
if(printTrimsLayers.length || singleLayers.length){
|
||||||
await this.createPrintTrimsLayers(printTrimsLayers, singleLayers);
|
await this.createPrintTrimsLayers(printTrimsLayers, singleLayers);
|
||||||
}
|
}
|
||||||
await this.changeCanvas();
|
await this.changeCanvas();
|
||||||
|
console.log("==========创建其他图层成功");
|
||||||
|
resolve();
|
||||||
|
this.awaitCanvasRun = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置画布对象的裁剪信息
|
// 设置画布对象的裁剪信息
|
||||||
@@ -1508,49 +1657,65 @@ export class CanvasManager {
|
|||||||
let name = item.name;
|
let name = item.name;
|
||||||
let image = await new Promise(resolve => {
|
let image = await new Promise(resolve => {
|
||||||
fabric.Image.fromURL(item.path, (fabricImage)=>{
|
fabric.Image.fromURL(item.path, (fabricImage)=>{
|
||||||
const left = flLeft - flWidth * flScaleX / 2 + (item.location?.[0] || 0) * flScaleX
|
resolve(fabricImage);
|
||||||
const top = flTop - flHeight * flScaleY / 2 + (item.location?.[1] || 0) * flScaleY
|
}, { crossOrigin: "anonymous" });
|
||||||
const scaleX = flWidth * (item.scale?.[0] || 1) / fabricImage.width * flScaleX
|
})
|
||||||
const scaleY = flHeight * (item.scale?.[1] || 1) / fabricImage.height * flScaleY
|
let left = flLeft - flWidth * flScaleX / 2 + (item.location?.[0] || 0) * flScaleX
|
||||||
const {x, y} = calculateRotatedTopLeftDeg(
|
let top = flTop - flHeight * flScaleY / 2 + (item.location?.[1] || 0) * flScaleY
|
||||||
fabricImage.width * scaleX,
|
let scaleX = flWidth * (item.scale?.[0] || 1) / image.width * flScaleX
|
||||||
fabricImage.height * scaleY,
|
let scaleY = flHeight * (item.scale?.[1] || 1) / image.height * flScaleY
|
||||||
|
let {x, y} = calculateRotatedTopLeftDeg(
|
||||||
|
image.width * scaleX,
|
||||||
|
image.height * scaleY,
|
||||||
left,
|
left,
|
||||||
top,
|
top,
|
||||||
0,
|
0,
|
||||||
item.angle || 0
|
item.angle || 0
|
||||||
)
|
)
|
||||||
const angle = item.angle || 0
|
let angle = item.angle || 0
|
||||||
fabricImage.set({
|
|
||||||
|
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,
|
left: x,
|
||||||
top: y,
|
top: y,
|
||||||
scaleX: scaleX,
|
scaleX: scaleX,
|
||||||
scaleY: scaleY,
|
scaleY: scaleY,
|
||||||
angle: angle,
|
angle: angle,
|
||||||
|
opacity: opacity,
|
||||||
|
flipX: flipX,
|
||||||
|
flipY: flipY,
|
||||||
|
globalCompositeOperation: blendMode,
|
||||||
id: id,
|
id: id,
|
||||||
layerId: id,
|
layerId: id,
|
||||||
layerName: name,
|
layerName: name,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
hasControls: true,
|
hasControls: true,
|
||||||
hasBorders: true,
|
hasBorders: true,
|
||||||
specialType: SpecialType.PRINT_TRIMS_O,
|
isPrintTrims: true,
|
||||||
globalCompositeOperation: BlendMode.MULTIPLY,
|
|
||||||
});
|
});
|
||||||
resolve(fabricImage);
|
// this.canvas.add(image);
|
||||||
}, { crossOrigin: "anonymous" });
|
|
||||||
})
|
|
||||||
this.canvas.add(image);
|
|
||||||
let layer = createLayer({
|
let layer = createLayer({
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
type: LayerType.BITMAP,
|
type: LayerType.BITMAP,
|
||||||
visible: true,
|
visible: true,
|
||||||
locked: false,
|
locked: false,
|
||||||
opacity: 1.0,
|
opacity: opacity,
|
||||||
specialType: SpecialType.PRINT_TRIMS_L,
|
isPrintTrims: true,
|
||||||
blendMode: BlendMode.MULTIPLY,
|
blendMode: blendMode,
|
||||||
fabricObjects: [image.toObject(["id", "layerId", "layerName"])],
|
fabricObjects: [image.toObject(["id", "layerId", "layerName"])],
|
||||||
metadata: {sourceData: item},
|
metadata: {sourceData: item},
|
||||||
|
object: image,
|
||||||
})
|
})
|
||||||
children.push(layer);
|
children.push(layer);
|
||||||
};
|
};
|
||||||
@@ -1571,50 +1736,83 @@ export class CanvasManager {
|
|||||||
resolve(tcanvas);
|
resolve(tcanvas);
|
||||||
}, { crossOrigin: "anonymous" });
|
}, { crossOrigin: "anonymous" });
|
||||||
})
|
})
|
||||||
let scaleX = fixedLayerObj.width / image.width * (item.scale?.[0] || 1) / 5;
|
let scaleX_ = flWidth / image.width * (item.scale?.[0] || 1) / 5;
|
||||||
let scaleY = fixedLayerObj.height / image.height * (item.scale?.[1] || 1) / 5;
|
let scaleY_ = flHeight / image.height * (item.scale?.[1] || 1) / 5;
|
||||||
let scale = fixedLayerObj.width > fixedLayerObj.height ? scaleX : scaleY;
|
let scale = flWidth > flHeight ? scaleX_ : scaleY_;
|
||||||
let left = (item.location?.[0] || 0) - image.width * scale / 2
|
let offsetX = (item.location?.[0] || 0) - image.width * scale / 2
|
||||||
let top = (item.location?.[1] || 0) - image.height * scale / 2
|
let offsetY = (item.location?.[1] || 0) - image.height * scale / 2
|
||||||
|
let top = flTop - flHeight * flScaleY / 2
|
||||||
|
let 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({
|
let rect = new fabric.Rect({
|
||||||
id: id,
|
id: id,
|
||||||
layerId: id,
|
layerId: id,
|
||||||
layerName: name,
|
layerName: name,
|
||||||
width: fixedLayerObj.width,
|
width: flWidth,
|
||||||
height: fixedLayerObj.height,
|
height: flHeight,
|
||||||
top: fixedLayerObj.top - fixedLayerObj.height * fixedLayerObj.scaleY / 2,
|
top: top,
|
||||||
left: fixedLayerObj.left - fixedLayerObj.width * fixedLayerObj.scaleX / 2,
|
left: left,
|
||||||
scaleX: fixedLayerObj.scaleX,
|
scaleX: scaleX,
|
||||||
scaleY: fixedLayerObj.scaleY,
|
scaleY: scaleY,
|
||||||
globalCompositeOperation: BlendMode.MULTIPLY,
|
opacity: opacity,
|
||||||
|
angle: angle,
|
||||||
|
flipX: flipX,
|
||||||
|
flipY: flipY,
|
||||||
|
globalCompositeOperation: blendMode,
|
||||||
fill: new fabric.Pattern({
|
fill: new fabric.Pattern({
|
||||||
source: image,
|
source: fillSource,
|
||||||
repeat: "repeat",
|
repeat: fill_repeat,
|
||||||
patternTransform: createPatternTransform(scale, item.angle || 0),
|
patternTransform: createPatternTransform(scale, item.angle || 0),
|
||||||
offsetX: left, // 水平偏移
|
offsetX: offsetX, // 水平偏移
|
||||||
offsetY: top, // 垂直偏移
|
offsetY: offsetY, // 垂直偏移
|
||||||
}),
|
}),
|
||||||
fill_ : {
|
fill_ : {
|
||||||
source: item.path,
|
source: item.path,
|
||||||
gapX: 0,
|
gapX: gapX,
|
||||||
gapY: 0,
|
gapY: gapY,
|
||||||
width: image.width,
|
width: image.width,
|
||||||
height: image.height,
|
height: image.height,
|
||||||
},
|
},
|
||||||
specialType: SpecialType.REPEAT_O,
|
isPrintTrims: true,
|
||||||
});
|
});
|
||||||
this.canvas.add(rect);
|
// this.canvas.add(rect);
|
||||||
let layer = createLayer({
|
let layer = createLayer({
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
type: LayerType.BITMAP,
|
type: LayerType.BITMAP,
|
||||||
visible: true,
|
visible: true,
|
||||||
locked: true,
|
locked: false,
|
||||||
opacity: 1,
|
opacity: opacity,
|
||||||
specialType: SpecialType.REPEAT_L,
|
isPrintTrims: true,
|
||||||
blendMode: BlendMode.MULTIPLY,
|
blendMode: blendMode,
|
||||||
fabricObjects: [rect.toObject(["id", "layerId", "layerName"])],
|
fabricObjects: [rect.toObject(["id", "layerId", "layerName"])],
|
||||||
metadata: {sourceData: item},
|
metadata: {sourceData: item},
|
||||||
|
object: rect,
|
||||||
})
|
})
|
||||||
children.push(layer);
|
children.push(layer);
|
||||||
};
|
};
|
||||||
@@ -1630,7 +1828,14 @@ export class CanvasManager {
|
|||||||
// })
|
// })
|
||||||
// children.push(layer);
|
// children.push(layer);
|
||||||
// }
|
// }
|
||||||
// if(children.length === 0) return;
|
if(children.length === 0) return;
|
||||||
|
// 印花元素排序
|
||||||
|
if(new Set(children.map(v => v.metadata.sourceData.priority)).size === children.length){
|
||||||
|
children.sort((a, b) => b.metadata.sourceData.priority - a.metadata.sourceData.priority);
|
||||||
|
}
|
||||||
|
children.forEach(layer => {
|
||||||
|
this.canvas.add(layer.object);
|
||||||
|
});
|
||||||
const groupRect = new fabric.Rect({});
|
const groupRect = new fabric.Rect({});
|
||||||
await this.setObjecCliptInfo(groupRect);
|
await this.setObjecCliptInfo(groupRect);
|
||||||
// 插入组图层
|
// 插入组图层
|
||||||
@@ -1646,22 +1851,20 @@ export class CanvasManager {
|
|||||||
children: children,
|
children: children,
|
||||||
clippingMask: groupRect.toObject(),
|
clippingMask: groupRect.toObject(),
|
||||||
isPrintTrimsGroup: true,
|
isPrintTrimsGroup: true,
|
||||||
specialType: SpecialType.PRINT_TRIMS_G,
|
|
||||||
});
|
});
|
||||||
this.layers.value.splice(groupIndex, 0, groupLayer);
|
this.layers.value.splice(groupIndex, 0, groupLayer);
|
||||||
console.log("==========layers", [...this.layers.value]);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 画布事件变更后
|
* 画布事件变更后
|
||||||
*/
|
*/
|
||||||
async changeCanvas(){
|
async changeCanvas(fids = [], isBeforeChange = false){
|
||||||
|
if(!isBeforeChange) this.canvasChangeing = false;
|
||||||
const fixedLayerObj = this.getFixedLayerObject();
|
const fixedLayerObj = this.getFixedLayerObject();
|
||||||
if(!fixedLayerObj) return console.warn("固定图层对象不存在", fixedLayerObj)
|
if(!fixedLayerObj) return console.warn("固定图层对象不存在", fixedLayerObj)
|
||||||
const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
|
const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
|
||||||
if(colorObject){
|
if(colorObject){
|
||||||
const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP);
|
const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP).filter(id => !fids.includes(id));
|
||||||
if(ids.length === 0){
|
if(ids.length === 0){
|
||||||
ids.unshift(SpecialLayerId.SPECIAL_GROUP);
|
ids.unshift(SpecialLayerId.SPECIAL_GROUP);
|
||||||
await this.setObjecCliptInfo(colorObject);
|
await this.setObjecCliptInfo(colorObject);
|
||||||
@@ -1681,6 +1884,24 @@ export class CanvasManager {
|
|||||||
this.canvas.renderAll();
|
this.canvas.renderAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/** 画布变更之前 */
|
||||||
|
async beforeChangeCanvas(objects){
|
||||||
|
if(this.canvasChangeing) return;
|
||||||
|
const ids = objects.filter(v => {
|
||||||
|
return v.isPrintTrims && v.globalCompositeOperation && v.globalCompositeOperation !== BlendMode.NORMAL
|
||||||
|
}).map(v => v.layerId);
|
||||||
|
if(ids.length > 0){
|
||||||
|
this.canvasChangeing = true;
|
||||||
|
this.canvas.getObjects().forEach(v => {
|
||||||
|
if(ids.includes(v.layerId)){
|
||||||
|
v.globalCompositeOperation_ = v.globalCompositeOperation;
|
||||||
|
v.globalCompositeOperation = BlendMode.NORMAL;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.canvas.renderAll();
|
||||||
|
await this.changeCanvas(ids, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缩放红绿图模式内容以适应当前画布大小
|
* 缩放红绿图模式内容以适应当前画布大小
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export class ExportManager {
|
|||||||
* @param {Boolean} options.isContainBg 是否包含背景图层
|
* @param {Boolean} options.isContainBg 是否包含背景图层
|
||||||
* @param {Boolean} options.isContainFixed 是否包含固定图层
|
* @param {Boolean} options.isContainFixed 是否包含固定图层
|
||||||
* @param {Boolean} options.isContainFixedOther 是否包含其他固定图层
|
* @param {Boolean} options.isContainFixedOther 是否包含其他固定图层
|
||||||
|
* @param {Boolean} options.isContainNormalLayer 是否包含普通图层
|
||||||
* @param {Boolean} options.isCropByBg 是否使用背景大小裁剪
|
* @param {Boolean} options.isCropByBg 是否使用背景大小裁剪
|
||||||
* @param {String} options.layerId 导出具体图层ID
|
* @param {String} options.layerId 导出具体图层ID
|
||||||
* @param {Array} options.layerIdArray 导出多个图层ID数组
|
* @param {Array} options.layerIdArray 导出多个图层ID数组
|
||||||
@@ -35,6 +36,7 @@ export class ExportManager {
|
|||||||
isContainBg = false,
|
isContainBg = false,
|
||||||
isContainFixed = false,
|
isContainFixed = false,
|
||||||
isContainFixedOther = false, // 是否包含其他固定图层
|
isContainFixedOther = false, // 是否包含其他固定图层
|
||||||
|
isContainNormalLayer = true, // 是否包含普通图层
|
||||||
isCropByBg = false, // 是否使用背景大小裁剪
|
isCropByBg = false, // 是否使用背景大小裁剪
|
||||||
layerId = "",
|
layerId = "",
|
||||||
layerIdArray = [],
|
layerIdArray = [],
|
||||||
@@ -68,6 +70,7 @@ export class ExportManager {
|
|||||||
isContainBg,
|
isContainBg,
|
||||||
isContainFixed,
|
isContainFixed,
|
||||||
isContainFixedOther, // 是否包含其他固定图层
|
isContainFixedOther, // 是否包含其他固定图层
|
||||||
|
isContainNormalLayer, // 是否包含普通图层
|
||||||
isRedGreenMode,
|
isRedGreenMode,
|
||||||
restoreOpacityInRedGreen,
|
restoreOpacityInRedGreen,
|
||||||
isCropByBg,
|
isCropByBg,
|
||||||
@@ -81,6 +84,7 @@ export class ExportManager {
|
|||||||
isContainBg,
|
isContainBg,
|
||||||
isContainFixed,
|
isContainFixed,
|
||||||
isContainFixedOther, // 是否包含其他固定图层
|
isContainFixedOther, // 是否包含其他固定图层
|
||||||
|
isContainNormalLayer, // 是否包含普通图层
|
||||||
isRedGreenMode,
|
isRedGreenMode,
|
||||||
restoreOpacityInRedGreen,
|
restoreOpacityInRedGreen,
|
||||||
isCropByBg,
|
isCropByBg,
|
||||||
@@ -160,6 +164,7 @@ export class ExportManager {
|
|||||||
* @param {Boolean} isContainBg 是否包含背景图层
|
* @param {Boolean} isContainBg 是否包含背景图层
|
||||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||||
|
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||||
* @param {Boolean} isRedGreenMode 是否为红绿图模式
|
* @param {Boolean} isRedGreenMode 是否为红绿图模式
|
||||||
* @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
|
* @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
|
||||||
* @param {Boolean} isCropByBg 是否使用背景大小裁剪
|
* @param {Boolean} isCropByBg 是否使用背景大小裁剪
|
||||||
@@ -173,6 +178,7 @@ export class ExportManager {
|
|||||||
isContainBg,
|
isContainBg,
|
||||||
isContainFixed,
|
isContainFixed,
|
||||||
isContainFixedOther, // 是否包含其他固定图层
|
isContainFixedOther, // 是否包含其他固定图层
|
||||||
|
isContainNormalLayer = true, // 是否包含普通图层
|
||||||
isRedGreenMode,
|
isRedGreenMode,
|
||||||
restoreOpacityInRedGreen,
|
restoreOpacityInRedGreen,
|
||||||
isCropByBg, // 是否使用背景大小裁剪
|
isCropByBg, // 是否使用背景大小裁剪
|
||||||
@@ -188,6 +194,7 @@ export class ExportManager {
|
|||||||
isContainBg,
|
isContainBg,
|
||||||
isContainFixed,
|
isContainFixed,
|
||||||
isContainFixedOther, // 是否包含其他固定图层
|
isContainFixedOther, // 是否包含其他固定图层
|
||||||
|
isContainNormalLayer, // 是否包含普通图层
|
||||||
);
|
);
|
||||||
|
|
||||||
if (objectsToExport.length === 0) {
|
if (objectsToExport.length === 0) {
|
||||||
@@ -220,6 +227,7 @@ export class ExportManager {
|
|||||||
* @param {Boolean} isContainBg 是否包含背景图层
|
* @param {Boolean} isContainBg 是否包含背景图层
|
||||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||||
|
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||||
* @param {Boolean} isRedGreenMode 是否为红绿图模式
|
* @param {Boolean} isRedGreenMode 是否为红绿图模式
|
||||||
* @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
|
* @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
|
||||||
* @param {Boolean} isCropByBg 是否使用背景大小裁剪
|
* @param {Boolean} isCropByBg 是否使用背景大小裁剪
|
||||||
@@ -233,6 +241,7 @@ export class ExportManager {
|
|||||||
isContainBg,
|
isContainBg,
|
||||||
isContainFixed,
|
isContainFixed,
|
||||||
isContainFixedOther, // 是否包含其他固定图层
|
isContainFixedOther, // 是否包含其他固定图层
|
||||||
|
isContainNormalLayer, // 是否包含普通图层
|
||||||
isRedGreenMode,
|
isRedGreenMode,
|
||||||
restoreOpacityInRedGreen,
|
restoreOpacityInRedGreen,
|
||||||
isCropByBg, // 是否使用背景大小裁剪
|
isCropByBg, // 是否使用背景大小裁剪
|
||||||
@@ -246,6 +255,7 @@ export class ExportManager {
|
|||||||
isContainBg,
|
isContainBg,
|
||||||
isContainFixed,
|
isContainFixed,
|
||||||
isContainFixedOther, // 是否包含其他固定图层
|
isContainFixedOther, // 是否包含其他固定图层
|
||||||
|
isContainNormalLayer, // 是否包含普通图层
|
||||||
excludedLayers,
|
excludedLayers,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -405,11 +415,12 @@ export class ExportManager {
|
|||||||
* @param {Boolean} isContainBg 是否包含背景图层
|
* @param {Boolean} isContainBg 是否包含背景图层
|
||||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||||
|
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||||
* @param {Array} excludedLayers 排除的图层ID数组
|
* @param {Array} excludedLayers 排除的图层ID数组
|
||||||
* @returns {Array} 按正确顺序排列的真实对象数组
|
* @returns {Array} 按正确顺序排列的真实对象数组
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_collectObjectsByLayerOrder(layerIdArray, isContainBg, isContainFixed, isContainFixedOther, excludedLayers) {
|
_collectObjectsByLayerOrder(layerIdArray, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer, excludedLayers) {
|
||||||
const objectsToExport = [];
|
const objectsToExport = [];
|
||||||
const allLayers = this._getAllLayersFlattened(excludedLayers); // 获取扁平化的图层列表
|
const allLayers = this._getAllLayersFlattened(excludedLayers); // 获取扁平化的图层列表
|
||||||
|
|
||||||
@@ -421,7 +432,7 @@ export class ExportManager {
|
|||||||
if (layerIdArray && !layerIdArray.includes(layer.id)) continue;
|
if (layerIdArray && !layerIdArray.includes(layer.id)) continue;
|
||||||
|
|
||||||
// 检查图层类型过滤条件
|
// 检查图层类型过滤条件
|
||||||
if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther))
|
if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (layer.visible) {
|
if (layer.visible) {
|
||||||
@@ -670,7 +681,7 @@ export class ExportManager {
|
|||||||
isEnhanceImg, // 是否是增强图片
|
isEnhanceImg, // 是否是增强图片
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("导出图片数据URL:", dataURL);
|
// console.log("导出图片数据URL:", dataURL);
|
||||||
return dataURL;
|
return dataURL;
|
||||||
|
|
||||||
// // 创建与画布相同尺寸的临时画布
|
// // 创建与画布相同尺寸的临时画布
|
||||||
@@ -1040,10 +1051,11 @@ export class ExportManager {
|
|||||||
* @param {Boolean} isContainBg 是否包含背景图层
|
* @param {Boolean} isContainBg 是否包含背景图层
|
||||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||||
|
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||||
* @returns {Boolean} 是否应该包含
|
* @returns {Boolean} 是否应该包含
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther) {
|
_shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer) {
|
||||||
if (!layer) return false;
|
if (!layer) return false;
|
||||||
|
|
||||||
// 检查背景图层
|
// 检查背景图层
|
||||||
@@ -1061,7 +1073,12 @@ export class ExportManager {
|
|||||||
return isContainFixedOther;
|
return isContainFixedOther;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 普通图层总是包含
|
// 印花图层始终导出
|
||||||
|
if (layer.isPrintTrims || layer.isPrintTrimsGroup) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 普通图层
|
||||||
|
return isContainNormalLayer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export class LayerManager {
|
|||||||
*/
|
*/
|
||||||
initCommandManager() {
|
initCommandManager() {
|
||||||
// 命令注册逻辑已移除,现在直接使用命令类实例化和执行
|
// 命令注册逻辑已移除,现在直接使用命令类实例化和执行
|
||||||
console.log("CommandManager 已初始化,使用直接命令调用模式");
|
// console.log("CommandManager 已初始化,使用直接命令调用模式");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,7 +161,7 @@ export class LayerManager {
|
|||||||
this.layerSort = createLayerSort(this.canvas, this.layers, {
|
this.layerSort = createLayerSort(this.canvas, this.layers, {
|
||||||
commandManager: this.commandManager,
|
commandManager: this.commandManager,
|
||||||
});
|
});
|
||||||
console.log("图层排序工具已初始化");
|
// console.log("图层排序工具已初始化");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ export class LayerManager {
|
|||||||
// 更新所有对象的交互性
|
// 更新所有对象的交互性
|
||||||
this.updateLayersObjectsInteractivity();
|
this.updateLayersObjectsInteractivity();
|
||||||
|
|
||||||
console.log(`已切换到${mode}模式`);
|
// console.log(`已切换到${mode}模式`);
|
||||||
}
|
}
|
||||||
|
|
||||||
setToolManager(toolManager) {
|
setToolManager(toolManager) {
|
||||||
@@ -332,7 +332,7 @@ export class LayerManager {
|
|||||||
|
|
||||||
// 私有方法:应用交互规则
|
// 私有方法:应用交互规则
|
||||||
async _applyInteractionRules({ isMoveing }) {
|
async _applyInteractionRules({ isMoveing }) {
|
||||||
console.log("updateLayersObjectsInteractivity ===>", this.editorMode);
|
// console.log("updateLayersObjectsInteractivity ===>", this.editorMode);
|
||||||
const objects = this.canvas.getObjects();
|
const objects = this.canvas.getObjects();
|
||||||
const editorMode = this.editorMode || CanvasConfig.defaultTool;
|
const editorMode = this.editorMode || CanvasConfig.defaultTool;
|
||||||
const layers = this.layers?.value || [];
|
const layers = this.layers?.value || [];
|
||||||
@@ -697,6 +697,7 @@ export class LayerManager {
|
|||||||
* 初始化图层,确保有背景层、固定图层和一个空白图层
|
* 初始化图层,确保有背景层、固定图层和一个空白图层
|
||||||
*/
|
*/
|
||||||
async initializeLayers() {
|
async initializeLayers() {
|
||||||
|
console.log("初始化图层",this.layers.value.length)
|
||||||
// 如果没有任何图层,创建背景层、固定图层和一个空白图层
|
// 如果没有任何图层,创建背景层、固定图层和一个空白图层
|
||||||
if (this.layers.value.length === 0) {
|
if (this.layers.value.length === 0) {
|
||||||
// 创建背景图层
|
// 创建背景图层
|
||||||
@@ -975,7 +976,7 @@ export class LayerManager {
|
|||||||
return !layer.isFixed && !layer.isFixedOther && !layer.isBackground
|
return !layer.isFixed && !layer.isFixedOther && !layer.isBackground
|
||||||
})
|
})
|
||||||
// const normalLayers = this.layers.value.filter((l) => !l.isBackground && !l.isFixed && !l.isFixedOther);
|
// 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
|
if (isChild ? parentLength <= 1 : false) {//normalLayers.length <= 1
|
||||||
console.warn("不能删除唯一的普通图层");
|
console.warn("不能删除唯一的普通图层");
|
||||||
message.warning(this.t("Canvas.cannotDeleteOnlyLayer"));
|
message.warning(this.t("Canvas.cannotDeleteOnlyLayer"));
|
||||||
@@ -1096,14 +1097,14 @@ export class LayerManager {
|
|||||||
if (this.commandManager) {
|
if (this.commandManager) {
|
||||||
const result = this.commandManager.execute(command);
|
const result = this.commandManager.execute(command);
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(`✅ 图层移动成功: ${direction}`);
|
// console.log(`✅ 图层移动成功: ${direction}`);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
const result = command.execute();
|
const result = command.execute();
|
||||||
if (result) {
|
if (result) {
|
||||||
// 更新画布渲染顺序
|
// 更新画布渲染顺序
|
||||||
console.log(`✅ 图层移动成功: ${direction}`);
|
// console.log(`✅ 图层移动成功: ${direction}`);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1147,7 +1148,6 @@ export class LayerManager {
|
|||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
console.log("==========", allObjects)
|
|
||||||
// if (layer.fill) {
|
// if (layer.fill) {
|
||||||
// // 如果图层有填充颜色,设置所有对象的填充颜色
|
// // 如果图层有填充颜色,设置所有对象的填充颜色
|
||||||
// const { object } = findObjectById(this.canvas, layer.fill.id);
|
// const { object } = findObjectById(this.canvas, layer.fill.id);
|
||||||
@@ -1580,7 +1580,7 @@ export class LayerManager {
|
|||||||
/**
|
/**
|
||||||
* 排序图层,确保图层顺序: 普通图层 > 固定图层 > 背景图层
|
* 排序图层,确保图层顺序: 普通图层 > 固定图层 > 背景图层
|
||||||
*/
|
*/
|
||||||
sortLayers() {
|
async sortLayers() {
|
||||||
// 对图层进行排序:背景图层在最底层(数组最后),固定图层在中间
|
// 对图层进行排序:背景图层在最底层(数组最后),固定图层在中间
|
||||||
this.layers.value.sort((a, b) => {
|
this.layers.value.sort((a, b) => {
|
||||||
// 如果a是背景图层,它应该排在后面(最底层)
|
// 如果a是背景图层,它应该排在后面(最底层)
|
||||||
@@ -1604,17 +1604,17 @@ export class LayerManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 更新画布对象顺序
|
// 更新画布对象顺序
|
||||||
this._rearrangeObjects();
|
await this._rearrangeObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新排列画布上的对象以匹配图层顺序
|
* 重新排列画布上的对象以匹配图层顺序
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_rearrangeObjects() {
|
async _rearrangeObjects() {
|
||||||
if (this.layerSort) {
|
if (this.layerSort) {
|
||||||
// 使用LayerSort的高级排序
|
// 使用LayerSort的高级排序
|
||||||
this.layerSort.rearrangeObjects();
|
await this.layerSort.rearrangeObjects();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1750,7 +1750,7 @@ export class LayerManager {
|
|||||||
layer.serializedObjects = layer.fabricObjects
|
layer.serializedObjects = layer.fabricObjects
|
||||||
.map((obj) => {
|
.map((obj) => {
|
||||||
if (typeof obj.toObject === "function") {
|
if (typeof obj.toObject === "function") {
|
||||||
return obj.toObject(["id", "layerId", "layerName"]);
|
return obj.toObject(["id", "layerId", "layerName", "fill_"]);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
@@ -1763,7 +1763,7 @@ export class LayerManager {
|
|||||||
if (layer.fabricObject) {
|
if (layer.fabricObject) {
|
||||||
layer.serializedBackgroundObject =
|
layer.serializedBackgroundObject =
|
||||||
typeof layer.fabricObject.toObject === "function"
|
typeof layer.fabricObject.toObject === "function"
|
||||||
? layer.fabricObject.toObject(["id", "layerId", "layerName"])
|
? layer.fabricObject.toObject(["id", "layerId", "layerName", "fill_"])
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
delete layer.fabricObject;
|
delete layer.fabricObject;
|
||||||
@@ -1793,7 +1793,7 @@ export class LayerManager {
|
|||||||
return layer.fabricObjects
|
return layer.fabricObjects
|
||||||
.map((obj) => {
|
.map((obj) => {
|
||||||
const { object } = findObjectById(this.canvas, obj.id);
|
const { object } = findObjectById(this.canvas, obj.id);
|
||||||
if (object) return object.toObject(["id", "layerId", "layerName"]);
|
if (object) return object.toObject(["id", "layerId", "layerName", "fill_"]);
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
@@ -1839,13 +1839,14 @@ export class LayerManager {
|
|||||||
|
|
||||||
// 存储到剪贴板
|
// 存储到剪贴板
|
||||||
this.clipboardData = layerCopy;
|
this.clipboardData = layerCopy;
|
||||||
|
console.log("复制图层:", layerCopy);
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.value = "aida_copy_canvas_layer: " + layer.name;
|
input.value = "aida_copy_canvas_layer: " + layer.name;
|
||||||
document.body.appendChild(input);
|
document.body.appendChild(input);
|
||||||
input.select();
|
input.select();
|
||||||
document.execCommand("copy");
|
document.execCommand("copy");
|
||||||
document.body.removeChild(input);
|
document.body.removeChild(input);
|
||||||
console.log(`已复制图层:${layer.name}`);
|
// console.log(`已复制图层:${layer.name}`);
|
||||||
|
|
||||||
return this.clipboardData;
|
return this.clipboardData;
|
||||||
}
|
}
|
||||||
@@ -1870,7 +1871,7 @@ export class LayerManager {
|
|||||||
|
|
||||||
// 检查是否是唯一的普通图层
|
// 检查是否是唯一的普通图层
|
||||||
const normalLayers = this.layers.value.filter((l) => !l.isBackground && !l.isFixed && !l.isFixedOther);
|
const normalLayers = this.layers.value.filter((l) => !l.isBackground && !l.isFixed && !l.isFixedOther);
|
||||||
console.log("普通图层:", normalLayers)
|
// console.log("普通图层:", normalLayers)
|
||||||
if (normalLayers.length <= 1) {
|
if (normalLayers.length <= 1) {
|
||||||
console.warn("不能剪切唯一的普通图层");
|
console.warn("不能剪切唯一的普通图层");
|
||||||
return null;
|
return null;
|
||||||
@@ -1884,7 +1885,7 @@ export class LayerManager {
|
|||||||
layerCopy.serializedObjects = layer.fabricObjects
|
layerCopy.serializedObjects = layer.fabricObjects
|
||||||
.map((obj) =>
|
.map((obj) =>
|
||||||
typeof obj.toObject === "function"
|
typeof obj.toObject === "function"
|
||||||
? obj.toObject(["id", "layerId", "layerName"])
|
? obj.toObject(["id", "layerId", "layerName", "fill_"])
|
||||||
: null
|
: null
|
||||||
)
|
)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
@@ -1930,21 +1931,17 @@ export class LayerManager {
|
|||||||
// 更新对象交互性
|
// 更新对象交互性
|
||||||
this.updateLayersObjectsInteractivity();
|
this.updateLayersObjectsInteractivity();
|
||||||
|
|
||||||
console.log(`已剪切图层:${layer.name}`);
|
// console.log(`已剪切图层:${layer.name}`);
|
||||||
|
|
||||||
return this.clipboardData;
|
return this.clipboardData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 粘贴图层
|
|
||||||
* @returns {string} 新创建的图层ID
|
|
||||||
*/
|
|
||||||
/**
|
/**
|
||||||
* 粘贴图层
|
* 粘贴图层
|
||||||
* @returns {string} 新创建的图层ID
|
* @returns {string} 新创建的图层ID
|
||||||
*/
|
*/
|
||||||
async pasteLayer(event) {
|
async pasteLayer(event) {
|
||||||
console.log("剪贴板数据:", this.clipboardData,event);
|
// console.log("剪贴板数据:", this.clipboardData,event);
|
||||||
if (!this.clipboardData) {
|
if (!this.clipboardData) {
|
||||||
console.error("剪贴板中没有图层数据");
|
console.error("剪贴板中没有图层数据");
|
||||||
return null;
|
return null;
|
||||||
@@ -2099,7 +2096,7 @@ export class LayerManager {
|
|||||||
// 重新初始化基本图层
|
// 重新初始化基本图层
|
||||||
this.initializeLayers();
|
this.initializeLayers();
|
||||||
|
|
||||||
console.log("已清空画布");
|
// console.log("已清空画布");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2312,7 +2309,7 @@ export class LayerManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (emptyLayerIds.length > 0) {
|
if (emptyLayerIds.length > 0) {
|
||||||
console.log(`已清理 ${emptyLayerIds.length} 个空图层`);
|
// console.log(`已清理 ${emptyLayerIds.length} 个空图层`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return emptyLayerIds;
|
return emptyLayerIds;
|
||||||
@@ -2344,7 +2341,7 @@ export class LayerManager {
|
|||||||
this.canvasManager = null;
|
this.canvasManager = null;
|
||||||
this.toolManager = null;
|
this.toolManager = null;
|
||||||
|
|
||||||
console.log("LayerManager 已销毁");
|
// console.log("LayerManager 已销毁");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2652,13 +2649,13 @@ export class LayerManager {
|
|||||||
// 启用红绿图模式
|
// 启用红绿图模式
|
||||||
enableRedGreenMode() {
|
enableRedGreenMode() {
|
||||||
this.isRedGreenMode = true;
|
this.isRedGreenMode = true;
|
||||||
console.log("图层管理器:红绿图模式已启用");
|
// console.log("图层管理器:红绿图模式已启用");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 禁用红绿图模式
|
// 禁用红绿图模式
|
||||||
disableRedGreenMode() {
|
disableRedGreenMode() {
|
||||||
this.isRedGreenMode = false;
|
this.isRedGreenMode = false;
|
||||||
console.log("图层管理器:红绿图模式已禁用");
|
// console.log("图层管理器:红绿图模式已禁用");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否为红绿图模式
|
// 检查是否为红绿图模式
|
||||||
@@ -2708,14 +2705,14 @@ export class LayerManager {
|
|||||||
: LayerSortUtils.shouldUseAsyncProcessing(objectsCount, layersCount);
|
: LayerSortUtils.shouldUseAsyncProcessing(objectsCount, layersCount);
|
||||||
|
|
||||||
if (shouldUseAsync) {
|
if (shouldUseAsync) {
|
||||||
console.log(
|
// console.log(
|
||||||
`使用异步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
|
// `使用异步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
|
||||||
);
|
// );
|
||||||
return this.layerSort.rearrangeObjectsAsync();
|
return this.layerSort.rearrangeObjectsAsync();
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
// console.log(
|
||||||
`使用同步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
|
// `使用同步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
|
||||||
);
|
// );
|
||||||
this.layerSort.rearrangeObjects();
|
this.layerSort.rearrangeObjects();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2735,7 +2732,7 @@ export class LayerManager {
|
|||||||
const result = this.layerSort.smartSort(targetLayerIds);
|
const result = this.layerSort.smartSort(targetLayerIds);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log("智能排序完成");
|
// console.log("智能排序完成");
|
||||||
// 更新对象交互性
|
// 更新对象交互性
|
||||||
this.updateLayersObjectsInteractivity();
|
this.updateLayersObjectsInteractivity();
|
||||||
}
|
}
|
||||||
@@ -2761,9 +2758,9 @@ export class LayerManager {
|
|||||||
const stats = this.layerSort.optimizeLayerStructure();
|
const stats = this.layerSort.optimizeLayerStructure();
|
||||||
|
|
||||||
if (stats.removedEmptyLayers > 0 || stats.reorderedLayers > 0) {
|
if (stats.removedEmptyLayers > 0 || stats.reorderedLayers > 0) {
|
||||||
console.log(
|
// console.log(
|
||||||
`图层结构优化完成: 清理空图层 ${stats.removedEmptyLayers} 个, 重新排序 ${stats.reorderedLayers} 个图层`
|
// `图层结构优化完成: 清理空图层 ${stats.removedEmptyLayers} 个, 重新排序 ${stats.reorderedLayers} 个图层`
|
||||||
);
|
// );
|
||||||
// 更新对象交互性
|
// 更新对象交互性
|
||||||
this.updateLayersObjectsInteractivity();
|
this.updateLayersObjectsInteractivity();
|
||||||
}
|
}
|
||||||
@@ -2838,9 +2835,9 @@ export class LayerManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(
|
// console.log(
|
||||||
`图层 ${layerId} - oldIndex: ${oldIndex} 已移动到位置 ${newIndex}`
|
// `图层 ${layerId} - oldIndex: ${oldIndex} 已移动到位置 ${newIndex}`
|
||||||
);
|
// );
|
||||||
// 更新对象交互性
|
// 更新对象交互性
|
||||||
// this.updateLayersObjectsInteractivity();
|
// this.updateLayersObjectsInteractivity();
|
||||||
}
|
}
|
||||||
@@ -2917,9 +2914,9 @@ export class LayerManager {
|
|||||||
const result = this.layerSort.reorderLayers(oldIndex, newIndex, layerId);
|
const result = this.layerSort.reorderLayers(oldIndex, newIndex, layerId);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(
|
// console.log(
|
||||||
`高级排序完成: 图层 ${layerId} 从位置 ${oldIndex} 移动到 ${newIndex}`
|
// `高级排序完成: 图层 ${layerId} 从位置 ${oldIndex} 移动到 ${newIndex}`
|
||||||
);
|
// );
|
||||||
// 更新对象交互性
|
// 更新对象交互性
|
||||||
this.updateLayersObjectsInteractivity();
|
this.updateLayersObjectsInteractivity();
|
||||||
}
|
}
|
||||||
@@ -2944,9 +2941,9 @@ export class LayerManager {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(
|
// console.log(
|
||||||
`高级子图层排序完成: 图层 ${layerId} 在父图层 ${parentId} 中从位置 ${oldIndex} 移动到 ${newIndex}`
|
// `高级子图层排序完成: 图层 ${layerId} 在父图层 ${parentId} 中从位置 ${oldIndex} 移动到 ${newIndex}`
|
||||||
);
|
// );
|
||||||
// 重新排列画布对象
|
// 重新排列画布对象
|
||||||
this.rearrangeCanvasObjects();
|
this.rearrangeCanvasObjects();
|
||||||
}
|
}
|
||||||
@@ -2971,7 +2968,7 @@ export class LayerManager {
|
|||||||
// 重新排列画布对象
|
// 重新排列画布对象
|
||||||
this.rearrangeCanvasObjects();
|
this.rearrangeCanvasObjects();
|
||||||
|
|
||||||
console.log("使用LayerSort工具完成图层排序");
|
// console.log("使用LayerSort工具完成图层排序");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2979,7 +2976,7 @@ export class LayerManager {
|
|||||||
* 当图层顺序发生变化后调用此方法确保画布对象顺序正确
|
* 当图层顺序发生变化后调用此方法确保画布对象顺序正确
|
||||||
*/
|
*/
|
||||||
forceRebuildCanvasOrder() {
|
forceRebuildCanvasOrder() {
|
||||||
console.log("强制重建画布对象顺序");
|
// console.log("强制重建画布对象顺序");
|
||||||
|
|
||||||
if (this.layerSort) {
|
if (this.layerSort) {
|
||||||
// 使用LayerSort的高级排序
|
// 使用LayerSort的高级排序
|
||||||
@@ -3010,7 +3007,7 @@ export class LayerManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`开始批量重新排序 ${reorderOperations.length} 个图层`);
|
// console.log(`开始批量重新排序 ${reorderOperations.length} 个图层`);
|
||||||
|
|
||||||
let allSuccessful = true;
|
let allSuccessful = true;
|
||||||
|
|
||||||
@@ -3042,7 +3039,7 @@ export class LayerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (allSuccessful) {
|
if (allSuccessful) {
|
||||||
console.log("批量图层排序完成");
|
// console.log("批量图层排序完成");
|
||||||
} else {
|
} else {
|
||||||
console.warn("批量图层排序部分失败");
|
console.warn("批量图层排序部分失败");
|
||||||
}
|
}
|
||||||
@@ -3079,11 +3076,11 @@ export class LayerManager {
|
|||||||
// 执行命令
|
// 执行命令
|
||||||
if (this.commandManager) {
|
if (this.commandManager) {
|
||||||
const result = await this.commandManager.execute(command);
|
const result = await this.commandManager.execute(command);
|
||||||
result && console.log(`✅ 成功合并组图层: ${groupLayer.name}`);
|
// result && console.log(`✅ 成功合并组图层: ${groupLayer.name}`);
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
const result = await command.execute();
|
const result = await command.execute();
|
||||||
result && console.log(`✅ 成功合并组图层: ${groupLayer.name}`);
|
// result && console.log(`✅ 成功合并组图层: ${groupLayer.name}`);
|
||||||
return result || [];
|
return result || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3132,13 +3129,13 @@ export class LayerManager {
|
|||||||
if (this.commandManager) {
|
if (this.commandManager) {
|
||||||
const result = await this.commandManager.execute(command);
|
const result = await this.commandManager.execute(command);
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(`✅ 成功栅格化图层: ${targetLayer.name}`);
|
// console.log(`✅ 成功栅格化图层: ${targetLayer.name}`);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
const result = await command.execute();
|
const result = await command.execute();
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(`✅ 成功栅格化图层: ${targetLayer.name}`);
|
// console.log(`✅ 成功栅格化图层: ${targetLayer.name}`);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -3209,13 +3206,13 @@ export class LayerManager {
|
|||||||
if (this.commandManager) {
|
if (this.commandManager) {
|
||||||
const result = await this.commandManager.execute(command);
|
const result = await this.commandManager.execute(command);
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
// console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
const result = await command.execute();
|
const result = await command.execute();
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
// console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -3259,7 +3256,7 @@ export class LayerManager {
|
|||||||
// 执行命令
|
// 执行命令
|
||||||
const result = await command.execute(false);
|
const result = await command.execute(false);
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
// console.log(`✅ 成功导出图层: ${targetLayer.name}`);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -3294,12 +3291,12 @@ export class LayerManager {
|
|||||||
const isSginleObject = e.target === activeSelection?._objects?.[0];
|
const isSginleObject = e.target === activeSelection?._objects?.[0];
|
||||||
if (e.target === activeSelection || isSginleObject) {
|
if (e.target === activeSelection || isSginleObject) {
|
||||||
hasMoved = false; // 重置移动状态
|
hasMoved = false; // 重置移动状态
|
||||||
console.log("🎯 开始移动组选择对象");
|
// console.log("🎯 开始移动组选择对象");
|
||||||
// 记录遮罩初始位置
|
// 记录遮罩初始位置
|
||||||
console.log(
|
// console.log(
|
||||||
"🖼️ 记录遮罩初始位置",
|
// "🖼️ 记录遮罩初始位置",
|
||||||
`${layer.clippingMask.left || 0}, ${layer.clippingMask.top || 0}`
|
// `${layer.clippingMask.left || 0}, ${layer.clippingMask.top || 0}`
|
||||||
);
|
// );
|
||||||
// 记录初始位置
|
// 记录初始位置
|
||||||
initialLeft = isSginleObject ? e.target.left : activeSelection.left;
|
initialLeft = isSginleObject ? e.target.left : activeSelection.left;
|
||||||
initialTop = isSginleObject ? e.target.top : activeSelection.top;
|
initialTop = isSginleObject ? e.target.top : activeSelection.top;
|
||||||
@@ -3366,12 +3363,12 @@ export class LayerManager {
|
|||||||
const isSginleObject = e.target === activeSelection?._objects?.[0];
|
const isSginleObject = e.target === activeSelection?._objects?.[0];
|
||||||
if (isSginleObject) {
|
if (isSginleObject) {
|
||||||
// 如果是单个对象,不处理
|
// 如果是单个对象,不处理
|
||||||
console.log("🚫 单个对象不处理移动完成");
|
// console.log("🚫 单个对象不处理移动完成");
|
||||||
hasMoved = false; // 重置移动状态
|
hasMoved = false; // 重置移动状态
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((target === activeSelection || isSginleObject) && hasMoved) {
|
if ((target === activeSelection || isSginleObject) && hasMoved) {
|
||||||
console.log("✅ 组选择对象移动完成");
|
// console.log("✅ 组选择对象移动完成");
|
||||||
|
|
||||||
// 计算最终移动距离
|
// 计算最终移动距离
|
||||||
const deltaX = target.left - initialLeft;
|
const deltaX = target.left - initialLeft;
|
||||||
@@ -3410,7 +3407,7 @@ export class LayerManager {
|
|||||||
// 鼠标抬起事件处理 - 备用方案
|
// 鼠标抬起事件处理 - 备用方案
|
||||||
const handleMouseUp = (e) => {
|
const handleMouseUp = (e) => {
|
||||||
if (hasMoved && this.canvas.getActiveObject() === activeSelection) {
|
if (hasMoved && this.canvas.getActiveObject() === activeSelection) {
|
||||||
console.log("🖱️ 鼠标抬起 - 备用移动完成处理");
|
// console.log("🖱️ 鼠标抬起 - 备用移动完成处理");
|
||||||
handleModified(e);
|
handleModified(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -3424,7 +3421,7 @@ export class LayerManager {
|
|||||||
this.canvas.off("selection:cleared", cleanup);
|
this.canvas.off("selection:cleared", cleanup);
|
||||||
this.canvas.off("selection:updated", 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:cleared", cleanup);
|
||||||
this.canvas.on("selection:updated", cleanup);
|
this.canvas.on("selection:updated", cleanup);
|
||||||
|
|
||||||
console.log("🎨 已设置组遮罩移动同步 - 使用 object:modified 事件");
|
// console.log("🎨 已设置组遮罩移动同步 - 使用 object:modified 事件");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
666
src/component/Canvas/CanvasEditor/managers/PartManager.js
Normal file
@@ -0,0 +1,666 @@
|
|||||||
|
import { fabric } from "fabric-with-all";
|
||||||
|
import { traceImageContour, imageToCanvas } from "../utils/helper";
|
||||||
|
import { OperationType, SpecialLayerId } from "../utils/layerHelper";
|
||||||
|
import { LassoCutoutCommand } from "../commands/LassoCutoutCommand";
|
||||||
|
import addIcon from "@/assets/images/canvas/add.png";
|
||||||
|
import removeIcon from "@/assets/images/canvas/remove.png";
|
||||||
|
import { getObjectAlphaToCanvas } from "../utils/objectHelper";
|
||||||
|
import { Https } from "@/tool/https";
|
||||||
|
import { PartDrawCommand, PartPointDrawCommand } from "../commands/PartCommands";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部件选择管理器
|
||||||
|
*/
|
||||||
|
export class PartManager {
|
||||||
|
/**
|
||||||
|
* 创建部件选择管理器
|
||||||
|
* @param {Object} options 配置选项
|
||||||
|
* @param {Object} options.canvas fabric.js画布实例
|
||||||
|
* @param {Object} options.commandManager 命令管理器实例
|
||||||
|
* @param {Object} options.canvasManager 画布管理实例
|
||||||
|
* @param {Object} options.layerManager 图层管理实例
|
||||||
|
* @param {Object} options.toolManager 工具管理实例
|
||||||
|
*/
|
||||||
|
constructor(options = {}) {
|
||||||
|
this.canvas = options.canvas;
|
||||||
|
this.commandManager = options.commandManager;
|
||||||
|
this.selectionManager = options.selectionManager;
|
||||||
|
this.layerManager = options.layerManager;
|
||||||
|
this.canvasManager = options.canvasManager;
|
||||||
|
this.toolManager = options.toolManager;
|
||||||
|
this.props = options.props;
|
||||||
|
|
||||||
|
// 选区样式配置
|
||||||
|
this.selectionStyle = {
|
||||||
|
stroke: "#0096ff",
|
||||||
|
strokeWidth: 1,
|
||||||
|
strokeDashArray: [5, 5],
|
||||||
|
fill: "rgba(0, 150, 255, 0.1)",
|
||||||
|
strokeUniform: true, // 保持描边宽度不随缩放改变
|
||||||
|
// fill: "rgba(127, 255, 127, 0.3)",
|
||||||
|
// stroke: "#2AA81B",
|
||||||
|
// strokeWidth: 2,
|
||||||
|
// strokeDashArray: [8, 4],
|
||||||
|
// strokeLineCap: "round",// 折线端点样式
|
||||||
|
// strokeLineJoin: "bevel", // 折线连接样式
|
||||||
|
selectable: false,
|
||||||
|
evented: false,
|
||||||
|
excludeFromExport: true,
|
||||||
|
hoverCursor: "default",
|
||||||
|
moveCursor: "default",
|
||||||
|
};
|
||||||
|
// 状态
|
||||||
|
this.isActive = false;
|
||||||
|
|
||||||
|
// 不再直接绑定事件处理函数
|
||||||
|
this._mouseDownHandler = null;
|
||||||
|
this._mouseMoveHandler = null;
|
||||||
|
this._mouseUpHandler = null;
|
||||||
|
this._keyDownHandler = null;
|
||||||
|
|
||||||
|
// 选区相关的工具类型
|
||||||
|
this.tools = [
|
||||||
|
OperationType.PART,
|
||||||
|
OperationType.PART_RECTANGLE,
|
||||||
|
OperationType.PART_BRUSH,
|
||||||
|
OperationType.PART_ERASER,
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
// 当前工具
|
||||||
|
this.activeTool = this.toolManager.activeTool;
|
||||||
|
|
||||||
|
this.rgba = { r: 0, g: 255, b: 0, a: 200 };
|
||||||
|
this.partId = SpecialLayerId.PART_SELECTOR;
|
||||||
|
this.partGroup = null; // 当前选区对象
|
||||||
|
this.partCanvas = null;// 选区画布
|
||||||
|
this.rectangleObject = null; // 矩形对象
|
||||||
|
this.pointList = []; // 点位列表 存储点选坐标
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前工具
|
||||||
|
* @param {String} toolId 工具ID
|
||||||
|
*/
|
||||||
|
setCurrentTool(toolId) {
|
||||||
|
// 检查是否为选区工具
|
||||||
|
const wasActive = this.isActive;
|
||||||
|
this.isActive = this.tools.includes(toolId);
|
||||||
|
|
||||||
|
if (toolId === OperationType.PART_ERASER) {
|
||||||
|
this.setEraserTool();
|
||||||
|
}
|
||||||
|
// else if (toolId === OperationType.PART || toolId === OperationType.PART_RECTANGLE) {
|
||||||
|
// this.clearPointData();
|
||||||
|
// this.resetPartObject();
|
||||||
|
// }
|
||||||
|
// if (toolId === OperationType.PART_ERASER || toolId === OperationType.PART_BRUSH) {
|
||||||
|
// if (this.pointList.length > 0) {
|
||||||
|
// this.clearPointData();
|
||||||
|
// this.resetPartObject();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 如果从非选区工具切换到选区工具,初始化事件
|
||||||
|
if (!wasActive && this.isActive) {
|
||||||
|
this.initEvents();
|
||||||
|
this.createPartObject();
|
||||||
|
}
|
||||||
|
// 如果从选区工具切换到非选区工具,清理事件和选区
|
||||||
|
else if (wasActive && !this.isActive) {
|
||||||
|
this.cleanupEvents();
|
||||||
|
this.clearPartObject();
|
||||||
|
this.clearPointData();
|
||||||
|
}
|
||||||
|
// 如果从选区工具切换到选区工具,重置选区
|
||||||
|
else if (wasActive && this.isActive) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化选区相关事件 */
|
||||||
|
initEvents() {
|
||||||
|
if (!this.canvas || this._mouseDownHandler) return; // 避免重复初始化
|
||||||
|
|
||||||
|
// 保存实例引用,用于事件处理函数中
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
// 鼠标按下事件处理
|
||||||
|
this._mouseDownHandler = (options) => {
|
||||||
|
// 如果选区功能未激活,不处理事件
|
||||||
|
if (!this.isActive) return;
|
||||||
|
// 阻止事件冒泡,避免与 CanvasEventManager 冲突
|
||||||
|
options.e.stopPropagation();
|
||||||
|
switch (this.activeTool.value) {
|
||||||
|
case OperationType.PART:
|
||||||
|
this._pointDownkHandler(options);
|
||||||
|
break;
|
||||||
|
case OperationType.PART_RECTANGLE:
|
||||||
|
this._rectangleDownHandler(options);
|
||||||
|
break;
|
||||||
|
case OperationType.PART_BRUSH:
|
||||||
|
this._brushDownHandler(options);
|
||||||
|
break;
|
||||||
|
case OperationType.PART_ERASER:
|
||||||
|
this._eraseDownHandler(options);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 鼠标移动事件处理
|
||||||
|
this._mouseMoveHandler = (options) => {
|
||||||
|
// 如果选区功能未激活或没有正在绘制的对象,不处理事件
|
||||||
|
if (!this.isActive) return;
|
||||||
|
// 阻止事件冒泡
|
||||||
|
options.e.stopPropagation();
|
||||||
|
switch (this.activeTool.value) {
|
||||||
|
case OperationType.PART:
|
||||||
|
this._pointMoveHandler(options);
|
||||||
|
break;
|
||||||
|
case OperationType.PART_RECTANGLE:
|
||||||
|
this._rectangleMoveHandler(options);
|
||||||
|
break;
|
||||||
|
case OperationType.PART_BRUSH:
|
||||||
|
this._brushMoveHandler(options);
|
||||||
|
break;
|
||||||
|
case OperationType.PART_ERASER:
|
||||||
|
this._eraseMoveHandler(options);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 鼠标抬起事件处理
|
||||||
|
this._mouseUpHandler = (options) => {
|
||||||
|
// 如果选区功能未激活或没有正在绘制的对象,不处理事件
|
||||||
|
if (!this.isActive) return;
|
||||||
|
// 阻止事件冒泡
|
||||||
|
if (options && options.e) {
|
||||||
|
options.e.stopPropagation();
|
||||||
|
}
|
||||||
|
switch (this.activeTool.value) {
|
||||||
|
case OperationType.PART:
|
||||||
|
this._pointUpHandler(options);
|
||||||
|
break;
|
||||||
|
case OperationType.PART_RECTANGLE:
|
||||||
|
this._rectangleUpHandler(options);
|
||||||
|
break;
|
||||||
|
case OperationType.PART_BRUSH:
|
||||||
|
this._brushUpHandler(options);
|
||||||
|
break;
|
||||||
|
case OperationType.PART_ERASER:
|
||||||
|
this._eraseUpHandler(options);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 键盘事件处理
|
||||||
|
this._keyDownHandler = (event) => {
|
||||||
|
// 只在选区功能激活时处理键盘事件
|
||||||
|
if (!this.isActive) return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加事件监听
|
||||||
|
this.canvas.on("mouse:down", this._mouseDownHandler);
|
||||||
|
this.canvas.on("mouse:move", this._mouseMoveHandler);
|
||||||
|
this.canvas.on("mouse:up", this._mouseUpHandler);
|
||||||
|
|
||||||
|
// 添加键盘事件监听
|
||||||
|
document.addEventListener("keydown", this._keyDownHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 清理事件监听 */
|
||||||
|
cleanupEvents() {
|
||||||
|
if (!this.canvas) return;
|
||||||
|
|
||||||
|
// 移除事件监听
|
||||||
|
if (this._mouseDownHandler) {
|
||||||
|
this.canvas.off("mouse:down", this._mouseDownHandler);
|
||||||
|
this._mouseDownHandler = null;
|
||||||
|
}
|
||||||
|
if (this._mouseMoveHandler) {
|
||||||
|
this.canvas.off("mouse:move", this._mouseMoveHandler);
|
||||||
|
this._mouseMoveHandler = null;
|
||||||
|
}
|
||||||
|
if (this._mouseUpHandler) {
|
||||||
|
this.canvas.off("mouse:up", this._mouseUpHandler);
|
||||||
|
this._mouseUpHandler = null;
|
||||||
|
}
|
||||||
|
if (this._keyDownHandler) {
|
||||||
|
document.removeEventListener("keydown", this._keyDownHandler);
|
||||||
|
this._keyDownHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 点选工具模式下点击事件处理 */
|
||||||
|
_pointDownkHandler(options) { }
|
||||||
|
/** 点选工具模式下移动事件处理 */
|
||||||
|
_pointMoveHandler(options) { }
|
||||||
|
/** 点选工具模式下抬起事件处理 */
|
||||||
|
async _pointUpHandler(options) {
|
||||||
|
const button = options.button;
|
||||||
|
const isLeft = button === 1;// 左键1(添加) 右键3(删除)
|
||||||
|
const fixedObject = this.canvasManager.getFixedLayerObject();
|
||||||
|
if (!fixedObject) return console.warn("未找到固定图层");
|
||||||
|
const { x, y } = this.handleMousePosition(options, fixedObject);
|
||||||
|
const label = isLeft ? 1 : 0;
|
||||||
|
const points = [];
|
||||||
|
const labels = [];
|
||||||
|
const pointList = [...this.pointList];
|
||||||
|
pointList.forEach((item) => {
|
||||||
|
points.push([item.x, item.y]);
|
||||||
|
labels.push(item.label);
|
||||||
|
});
|
||||||
|
points.push([x, y]);
|
||||||
|
labels.push(label);
|
||||||
|
const url = await this.getSegAnythingImage({
|
||||||
|
type: "point",
|
||||||
|
points,
|
||||||
|
labels,
|
||||||
|
});
|
||||||
|
pointList.push({
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
label: label,
|
||||||
|
})
|
||||||
|
const image1 = await this.loadImageToObject(url);
|
||||||
|
const canvas = getObjectAlphaToCanvas(image1, null, 0, this.rgba);
|
||||||
|
this.partPointDrawCommand(pointList, canvas);
|
||||||
|
}
|
||||||
|
partPointDrawCommand(list, canvas) {
|
||||||
|
const cmd = new PartPointDrawCommand({
|
||||||
|
canvas: this.canvas,
|
||||||
|
partManager: this,
|
||||||
|
partCanvas: canvas,
|
||||||
|
pointList: [...list],
|
||||||
|
})
|
||||||
|
if (this.commandManager?.execute) {
|
||||||
|
this.commandManager.execute(cmd);
|
||||||
|
} else {
|
||||||
|
cmd.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async pointDrawPartCanvas(list, canvas) {
|
||||||
|
this.selectionManager.clearSelection();
|
||||||
|
const fixedObject = this.canvasManager.getFixedLayerObject();
|
||||||
|
if (!fixedObject) {
|
||||||
|
console.warn("未找到固定图层")
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.resetPartObject();
|
||||||
|
this.pointList = [...list];
|
||||||
|
this.partCanvas = canvas;
|
||||||
|
const image2 = new fabric.Image(canvas);
|
||||||
|
image2.set({
|
||||||
|
originX: fixedObject.originX,
|
||||||
|
originY: fixedObject.originY,
|
||||||
|
});
|
||||||
|
this.partGroup.add(image2);
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
const item = list[i];
|
||||||
|
const icon = await this.loadImageToObject(item.label === 1 ? addIcon : removeIcon);
|
||||||
|
let size = 20;
|
||||||
|
let scaleX = size / (icon.width * this.partGroup.scaleX);
|
||||||
|
let scaleY = size / (icon.height * this.partGroup.scaleY);
|
||||||
|
icon.set({
|
||||||
|
scaleX: scaleX,
|
||||||
|
scaleY: scaleY,
|
||||||
|
left: item.x - this.partGroup.width / 2,
|
||||||
|
top: item.y - this.partGroup.height / 2,
|
||||||
|
originX: fixedObject.originX,
|
||||||
|
originY: fixedObject.originY,
|
||||||
|
})
|
||||||
|
this.partGroup.add(icon);
|
||||||
|
}
|
||||||
|
console.log(this.partGroup);
|
||||||
|
this.canvas.renderAll();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/** 清空点选数据 */
|
||||||
|
clearPointData() {
|
||||||
|
this.pointList = [];
|
||||||
|
this.partCanvas = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** 框选工具模式下点击事件处理 */
|
||||||
|
_rectangleDownHandler(options) {
|
||||||
|
this.pointList = [];
|
||||||
|
const fixedObject = this.canvasManager.getFixedLayerObject();
|
||||||
|
if (!fixedObject) return console.warn("未找到固定图层");
|
||||||
|
const { x, y } = this.handleMousePosition(options, fixedObject);
|
||||||
|
this.pointList.push(x, y);
|
||||||
|
|
||||||
|
this.rectangleObject = new fabric.Rect({
|
||||||
|
left: x - fixedObject.width / 2,
|
||||||
|
top: y - fixedObject.height / 2,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
...this.selectionStyle,
|
||||||
|
fill: "transparent", // 在绘制过程中不显示填充
|
||||||
|
strokeUniform: true,
|
||||||
|
});
|
||||||
|
this.partGroup.add(this.rectangleObject);
|
||||||
|
this.canvas.renderAll();
|
||||||
|
}
|
||||||
|
/** 框选工具模式下移动事件处理 */
|
||||||
|
_rectangleMoveHandler(options) {
|
||||||
|
if (!this.rectangleObject) return console.warn("未找到框选对象");
|
||||||
|
const fixedObject = this.canvasManager.getFixedLayerObject();
|
||||||
|
if (!fixedObject) return console.warn("未找到固定图层");
|
||||||
|
const { x, y } = this.handleMousePosition(options, fixedObject);
|
||||||
|
this.rectangleObject.set({
|
||||||
|
width: x - this.rectangleObject.left - fixedObject.width / 2,
|
||||||
|
height: y - this.rectangleObject.top - fixedObject.height / 2,
|
||||||
|
});
|
||||||
|
this.canvas.renderAll();
|
||||||
|
}
|
||||||
|
/** 框选工具模式下抬起事件处理 */
|
||||||
|
async _rectangleUpHandler(options) {
|
||||||
|
if (this.rectangleObject) {
|
||||||
|
this.partGroup.remove(this.rectangleObject);
|
||||||
|
this.canvas.renderAll();
|
||||||
|
}
|
||||||
|
const fixedObject = this.canvasManager.getFixedLayerObject();
|
||||||
|
if (!fixedObject) return console.warn("未找到固定图层");
|
||||||
|
const { x, y } = this.handleMousePosition(options, fixedObject);
|
||||||
|
this.pointList.push(x, y);
|
||||||
|
|
||||||
|
if (this.pointList.length !== 4) return console.warn("框选工具选择区域必须是矩形");
|
||||||
|
const url = await this.getSegAnythingImage({
|
||||||
|
type: "box",
|
||||||
|
box: [...this.pointList],
|
||||||
|
});
|
||||||
|
const image = await this.loadImageToObject(url);
|
||||||
|
const data = this.partCanvas?.getContext("2d")?.getImageData(0, 0, this.partCanvas.width, this.partCanvas.height);
|
||||||
|
const canvas = getObjectAlphaToCanvas(image, data, 0, this.rgba, !!data);
|
||||||
|
this.partDrawCommand(canvas);
|
||||||
|
}
|
||||||
|
/** 获取分隔后图片 */
|
||||||
|
async getSegAnythingImage(obj) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.canvas.loading.value = true;
|
||||||
|
});
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// const user_id = store.state.UserHabit.userDetail.userId;
|
||||||
|
const user_id = 24299;
|
||||||
|
const data = {
|
||||||
|
image_path: this.props.clothingMinIOPath,
|
||||||
|
user_id,
|
||||||
|
...obj,
|
||||||
|
}
|
||||||
|
Https.axiosPost(Https.httpUrls.segAnything, data)
|
||||||
|
.then(response => {
|
||||||
|
this.canvas.loading.value = false;
|
||||||
|
if (response) {
|
||||||
|
resolve(response);
|
||||||
|
} else {
|
||||||
|
console.error("获取分隔后图片失败");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.canvas.loading.value = false;
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/** 处理鼠标点位 */
|
||||||
|
handleMousePosition(options, fixedObject) {
|
||||||
|
const pos = options.absolutePointer;
|
||||||
|
const { x, y } = options.absolutePointer;
|
||||||
|
const width = fixedObject.width * fixedObject.scaleX;
|
||||||
|
const height = fixedObject.height * fixedObject.scaleY;
|
||||||
|
const X = (x - (fixedObject.left - width / 2)) / fixedObject.scaleX;
|
||||||
|
const Y = (y - (fixedObject.top - height / 2)) / fixedObject.scaleY;
|
||||||
|
return {
|
||||||
|
x: Math.round(X),
|
||||||
|
y: Math.round(Y),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** 绘制工具模式下点击事件处理 */
|
||||||
|
_brushDownHandler(options) { }
|
||||||
|
/** 绘制工具模式下移动事件处理 */
|
||||||
|
_brushMoveHandler(options) { }
|
||||||
|
/** 绘制工具模式下抬起事件处理 */
|
||||||
|
_brushUpHandler(options) { }
|
||||||
|
/** 绘制模式添加画笔 */
|
||||||
|
async addDrawPartImage(fabricImage) {
|
||||||
|
const scaleX = fabricImage.scaleX / this.partGroup.scaleX;
|
||||||
|
const scaleY = fabricImage.scaleY / this.partGroup.scaleY;
|
||||||
|
const top = (fabricImage.top - this.partGroup.top) / this.partGroup.scaleY;
|
||||||
|
const left = (fabricImage.left - this.partGroup.left) / this.partGroup.scaleX;
|
||||||
|
fabricImage.set({
|
||||||
|
scaleX,
|
||||||
|
scaleY,
|
||||||
|
top: top + this.partGroup.height / 2,
|
||||||
|
left: left + this.partGroup.width / 2,
|
||||||
|
})
|
||||||
|
const tcanvas = new fabric.StaticCanvas(document.createElement("canvas"), {
|
||||||
|
width: this.partGroup.width,
|
||||||
|
height: this.partGroup.height,
|
||||||
|
enableRetinaScaling: false,
|
||||||
|
});
|
||||||
|
if (this.partCanvas) {
|
||||||
|
let image = new fabric.Image(this.partCanvas);
|
||||||
|
tcanvas.add(image)
|
||||||
|
}
|
||||||
|
tcanvas.add(fabricImage)
|
||||||
|
tcanvas.renderAll();
|
||||||
|
const canvas = getObjectAlphaToCanvas(tcanvas, null, 0, this.rgba);
|
||||||
|
this.partDrawCommand(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** 切换到擦除工具 */
|
||||||
|
setEraserTool() {
|
||||||
|
if (!this.canvas) return console.warn("未找到画布");
|
||||||
|
const objects = this.canvas.getObjects();
|
||||||
|
objects.forEach(obj => {
|
||||||
|
if (obj.id === this.partId) {
|
||||||
|
obj.set({
|
||||||
|
erasable: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/** 擦除工具模式下擦除完成事件处理 */
|
||||||
|
async onErasingEnd(affectedObjects) {
|
||||||
|
console.log("擦除完成", affectedObjects);
|
||||||
|
const tcanvas = new fabric.StaticCanvas(document.createElement("canvas"), {
|
||||||
|
width: this.partGroup.width,
|
||||||
|
height: this.partGroup.height,
|
||||||
|
enableRetinaScaling: false,
|
||||||
|
});
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
this.partGroup.clone((clone) => {
|
||||||
|
clone.set({
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
top: this.partGroup.height / 2,
|
||||||
|
left: this.partGroup.width / 2,
|
||||||
|
})
|
||||||
|
tcanvas.add(clone);
|
||||||
|
resolve(clone);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
tcanvas.renderAll();
|
||||||
|
const canvas = getObjectAlphaToCanvas(tcanvas, null, 0, this.rgba);
|
||||||
|
this.partDrawCommand(canvas);
|
||||||
|
}
|
||||||
|
/** 擦除工具模式下点击事件处理 */
|
||||||
|
_eraseDownHandler(options) {
|
||||||
|
}
|
||||||
|
/** 擦除工具模式下移动事件处理 */
|
||||||
|
_eraseMoveHandler(options) {
|
||||||
|
}
|
||||||
|
/** 擦除工具模式下抬起事件处理 */
|
||||||
|
_eraseUpHandler(options) {
|
||||||
|
|
||||||
|
}
|
||||||
|
partDrawCommand(canvas) {
|
||||||
|
const cmd = new PartDrawCommand({
|
||||||
|
canvas: this.canvas,
|
||||||
|
partManager: this,
|
||||||
|
partCanvas: canvas,
|
||||||
|
})
|
||||||
|
if (this.commandManager?.execute) {
|
||||||
|
this.commandManager.execute(cmd);
|
||||||
|
} else {
|
||||||
|
cmd.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** 绘制部件画布 */
|
||||||
|
drawPartCanvas(canvas) {
|
||||||
|
this.selectionManager.clearSelection();
|
||||||
|
this.partCanvas = canvas;
|
||||||
|
const image = new fabric.Image(canvas);
|
||||||
|
image.set({
|
||||||
|
originX: this.partGroup.originX,
|
||||||
|
originY: this.partGroup.originY,
|
||||||
|
erasable: true,
|
||||||
|
});
|
||||||
|
this.resetPartObject();
|
||||||
|
this.partGroup.add(image);
|
||||||
|
this.canvas.renderAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除指定ID的对象 */
|
||||||
|
removeObjectsById(id) {
|
||||||
|
const objects = this.canvas.getObjects().filter(obj => obj.id === id);
|
||||||
|
this.canvas.remove(...objects);
|
||||||
|
}
|
||||||
|
loadImageToObject(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fabric.Image.fromURL(url, (img) => {
|
||||||
|
resolve(img);
|
||||||
|
}, { crossOrigin: "anonymous" });// 防止污染
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置点位对象组 */
|
||||||
|
resetPartObject(render = false) {
|
||||||
|
this.clearPartObject();
|
||||||
|
this.createPartObject();
|
||||||
|
if (render) this.canvas.renderAll();
|
||||||
|
}
|
||||||
|
createPartObject() {
|
||||||
|
const fixedObject = this.canvasManager.getFixedLayerObject();
|
||||||
|
if (!fixedObject) return console.warn("未找到固定图层");
|
||||||
|
const group = new fabric.Group();
|
||||||
|
group.set({
|
||||||
|
id: this.partId,
|
||||||
|
opacity: 1,
|
||||||
|
left: fixedObject.left,
|
||||||
|
top: fixedObject.top,
|
||||||
|
width: fixedObject.width,
|
||||||
|
height: fixedObject.height,
|
||||||
|
scaleX: fixedObject.scaleX,
|
||||||
|
scaleY: fixedObject.scaleY,
|
||||||
|
originX: fixedObject.originX,
|
||||||
|
originY: fixedObject.originY,
|
||||||
|
selectable: false,
|
||||||
|
evented: false,
|
||||||
|
erasable: true,
|
||||||
|
})
|
||||||
|
this.canvas.add(group);
|
||||||
|
this.partGroup = group;
|
||||||
|
}
|
||||||
|
/** 清空点位对象组 */
|
||||||
|
clearPartObject() {
|
||||||
|
this.removeObjectsById(this.partId);
|
||||||
|
this.partGroup = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建当前选区 */
|
||||||
|
async createPart() {
|
||||||
|
if (!this.partCanvas) return console.warn("没有点位画布");
|
||||||
|
const fixedObject = this.canvasManager.getFixedLayerObject();
|
||||||
|
if (!fixedObject) return console.warn("未找到固定图层");
|
||||||
|
// const tcanvas = new fabric.StaticCanvas(document.createElement("canvas"), {
|
||||||
|
// width: fixedObject.width,
|
||||||
|
// height: fixedObject.height,
|
||||||
|
// enableRetinaScaling: false,
|
||||||
|
// });
|
||||||
|
// await new Promise((resolve, reject) => {
|
||||||
|
// fixedObject.clone((clone) => {
|
||||||
|
// const clipPath = new fabric.Image(this.partCanvas);
|
||||||
|
// clipPath.set({
|
||||||
|
// originX: fixedObject.originX,
|
||||||
|
// originY: fixedObject.originY,
|
||||||
|
// })
|
||||||
|
// clone.set({
|
||||||
|
// scaleX: 1,
|
||||||
|
// scaleY: 1,
|
||||||
|
// clipPath: clipPath,
|
||||||
|
// })
|
||||||
|
// tcanvas.add(clone);
|
||||||
|
// resolve(clone);
|
||||||
|
// })
|
||||||
|
// });
|
||||||
|
// tcanvas.renderAll();
|
||||||
|
|
||||||
|
const scaleY = fixedObject.scaleY
|
||||||
|
const scaleX = fixedObject.scaleX
|
||||||
|
const top = fixedObject.top - fixedObject.height * scaleY / 2;
|
||||||
|
const left = fixedObject.left - fixedObject.width * scaleX / 2;
|
||||||
|
const arr = traceImageContour(this.partCanvas);
|
||||||
|
let minX = fixedObject.width;
|
||||||
|
let minY = fixedObject.height;
|
||||||
|
const str = arr.map((v) => {
|
||||||
|
if (v.x < minX) minX = v.x;
|
||||||
|
if (v.y < minY) minY = v.y;
|
||||||
|
return `${v.x} ${v.y}`
|
||||||
|
}).join(" L ");
|
||||||
|
const path = new fabric.Path(`M ${str} z`);
|
||||||
|
path.set({
|
||||||
|
left: left + minX * scaleX,
|
||||||
|
top: top + minY * scaleY,
|
||||||
|
scaleX: scaleX,
|
||||||
|
scaleY: scaleY,
|
||||||
|
...this.selectionStyle,
|
||||||
|
});
|
||||||
|
this.clearPart();
|
||||||
|
this.selectionManager.setSelectionObject(path);
|
||||||
|
const cmd = new LassoCutoutCommand({
|
||||||
|
canvas: this.canvas,
|
||||||
|
layerManager: this.layerManager,
|
||||||
|
selectionManager: this.selectionManager,
|
||||||
|
toolManager: this.toolManager,
|
||||||
|
})
|
||||||
|
this.commandManager.execute(cmd)
|
||||||
|
}
|
||||||
|
/** 清空点位 */
|
||||||
|
clearPart() {
|
||||||
|
if (this.activeTool.value === OperationType.PART) {
|
||||||
|
this.partPointDrawCommand([], null);
|
||||||
|
} else {
|
||||||
|
this.pointList = [];
|
||||||
|
this.partDrawCommand(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理资源
|
||||||
|
*/
|
||||||
|
dispose() {
|
||||||
|
this.cleanupEvents();
|
||||||
|
this.clearPartObject();
|
||||||
|
this.clearPointData();
|
||||||
|
|
||||||
|
this.canvas = null;
|
||||||
|
this.commandManager = null;
|
||||||
|
this.layerManager = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ export class ThumbnailManager {
|
|||||||
const { layer } = findLayerRecursively(this.layers.value, layerId);
|
const { layer } = findLayerRecursively(this.layers.value, layerId);
|
||||||
|
|
||||||
if (!fabricObjects || fabricObjects.length === 0) {
|
if (!fabricObjects || fabricObjects.length === 0) {
|
||||||
console.warn("⚠️ 无法生成缩略图:没有可栅格化的对象 返回空缩略图");
|
// console.warn("⚠️ 无法生成缩略图:没有可栅格化的对象 返回空缩略图");
|
||||||
// 如果没有对象,返回默认缩略图
|
// 如果没有对象,返回默认缩略图
|
||||||
if (layer) {
|
if (layer) {
|
||||||
layer.thumbnailUrl = this.defaultThumbnail; // 更新图层对象的缩略图
|
layer.thumbnailUrl = this.defaultThumbnail; // 更新图层对象的缩略图
|
||||||
@@ -123,7 +123,7 @@ export class ThumbnailManager {
|
|||||||
*/
|
*/
|
||||||
_collectLayersAndObjects(layerId) {
|
_collectLayersAndObjects(layerId) {
|
||||||
if (!layerId) {
|
if (!layerId) {
|
||||||
console.warn("⚠️ 无效的图层ID,无法收集对象");
|
// console.warn("⚠️ 无效的图层ID,无法收集对象");
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,17 +199,17 @@ export class ThumbnailManager {
|
|||||||
// 提取排序后的对象
|
// 提取排序后的对象
|
||||||
const objectsToRasterize = objectsWithZIndex.map((item) => item.object);
|
const objectsToRasterize = objectsWithZIndex.map((item) => item.object);
|
||||||
|
|
||||||
console.log(
|
// console.log(
|
||||||
`📊 收集到 ${layersToRasterize.length} 个图层,${objectsToRasterize.length} 个对象进行栅格化`
|
// `📊 收集到 ${layersToRasterize.length} 个图层,${objectsToRasterize.length} 个对象进行栅格化`
|
||||||
);
|
// );
|
||||||
console.log(
|
// console.log(
|
||||||
"🔢 对象z-index顺序:",
|
// "🔢 对象z-index顺序:",
|
||||||
objectsWithZIndex.map((item) => ({
|
// objectsWithZIndex.map((item) => ({
|
||||||
id: item.object.id,
|
// id: item.object.id,
|
||||||
type: item.object.type,
|
// type: item.object.type,
|
||||||
zIndex: item.zIndex,
|
// zIndex: item.zIndex,
|
||||||
}))
|
// }))
|
||||||
);
|
// );
|
||||||
|
|
||||||
return objectsToRasterize;
|
return objectsToRasterize;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export class ToolManager {
|
|||||||
this.activeTool = options.activeTool || {
|
this.activeTool = options.activeTool || {
|
||||||
value: OperationType.SELECT,
|
value: OperationType.SELECT,
|
||||||
};
|
};
|
||||||
|
this.props = options.props;
|
||||||
|
|
||||||
// 红绿图模式状态
|
// 红绿图模式状态
|
||||||
this.isRedGreenMode = false;
|
this.isRedGreenMode = false;
|
||||||
@@ -190,30 +191,26 @@ export class ToolManager {
|
|||||||
[OperationType.PART]: {
|
[OperationType.PART]: {
|
||||||
name: "部件选取工具",
|
name: "部件选取工具",
|
||||||
icon: "part",
|
icon: "part",
|
||||||
cursor: "crosshair",
|
cursor: "default",
|
||||||
// setup: this.setupLassoTool.bind(this),
|
setup: this.setupPartTool.bind(this),
|
||||||
specialLayerDisabled: true,
|
|
||||||
},
|
},
|
||||||
[OperationType.PART_RECTANGLE]: {
|
[OperationType.PART_RECTANGLE]: {
|
||||||
name: "部件选取工具-矩形",
|
name: "部件选取工具-矩形",
|
||||||
icon: "part",
|
icon: "part",
|
||||||
cursor: "crosshair",
|
cursor: "crosshair",
|
||||||
// setup: this.setupRectangleLassoTool.bind(this),
|
setup: this.setupPartRectangleTool.bind(this),
|
||||||
specialLayerDisabled: true,
|
|
||||||
},
|
},
|
||||||
[OperationType.PART_BRUSH]: {
|
[OperationType.PART_BRUSH]: {
|
||||||
name: "部件选取工具-画笔",
|
name: "部件选取工具-画笔",
|
||||||
icon: "part",
|
icon: "part",
|
||||||
cursor: "crosshair",
|
cursor: "default",
|
||||||
// setup: this.setupEllipseLassoTool.bind(this),
|
setup: this.setupPartBrushTool.bind(this),
|
||||||
specialLayerDisabled: true,
|
|
||||||
},
|
},
|
||||||
[OperationType.PART_ERASER]: {
|
[OperationType.PART_ERASER]: {
|
||||||
name: "部件选取工具-橡皮擦",
|
name: "部件选取工具-橡皮擦",
|
||||||
icon: "part",
|
icon: "part",
|
||||||
cursor: "crosshair",
|
cursor: "default",
|
||||||
// setup: this.setupEllipseLassoTool.bind(this),
|
setup: this.setupPartEraserTool.bind(this),
|
||||||
specialLayerDisabled: true,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 红绿图模式专用工具
|
// 红绿图模式专用工具
|
||||||
@@ -423,15 +420,18 @@ export class ToolManager {
|
|||||||
|
|
||||||
// 设置工具特定的状态
|
// 设置工具特定的状态
|
||||||
if (tool && typeof tool.setup === "function") {
|
if (tool && typeof tool.setup === "function") {
|
||||||
console.log(`画布切换工具:${tool.name}(${toolId})`)
|
// console.log(`画布切换工具:${tool.name}(${toolId})`)
|
||||||
this.canvas.toolId = toolId;
|
this.canvas.toolId = toolId;
|
||||||
tool.setup();
|
tool.setup(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通知选区管理器工具已改变
|
// 通知选区管理器工具已改变
|
||||||
if (this.canvasManager && this.canvasManager.selectionManager) {
|
if (this.canvasManager && this.canvasManager.selectionManager) {
|
||||||
this.canvasManager.selectionManager.setCurrentTool(toolId);
|
this.canvasManager.selectionManager.setCurrentTool(toolId);
|
||||||
}
|
}
|
||||||
|
if (this.canvasManager && this.canvasManager.partManager) {
|
||||||
|
this.canvasManager.partManager.setCurrentTool(toolId);
|
||||||
|
}
|
||||||
|
|
||||||
// 通知观察者
|
// 通知观察者
|
||||||
this.notifyObservers(toolId);
|
this.notifyObservers(toolId);
|
||||||
@@ -482,7 +482,7 @@ export class ToolManager {
|
|||||||
|
|
||||||
// 如有必要,可以调用当前工具的setup方法来全面恢复状态
|
// 如有必要,可以调用当前工具的setup方法来全面恢复状态
|
||||||
if (tool && typeof tool.setup === "function") {
|
if (tool && typeof tool.setup === "function") {
|
||||||
tool.setup();
|
tool.setup(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,7 +502,6 @@ export class ToolManager {
|
|||||||
if (!this.canvas) return;
|
if (!this.canvas) return;
|
||||||
this.canvas.isDrawingMode = false;
|
this.canvas.isDrawingMode = false;
|
||||||
this.canvas.selection = true;
|
this.canvas.selection = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -511,7 +510,7 @@ export class ToolManager {
|
|||||||
*/
|
*/
|
||||||
checkToolCanOperateSelectedObject() {
|
checkToolCanOperateSelectedObject() {
|
||||||
const layer = this.layerManager?.getActiveLayer();
|
const layer = this.layerManager?.getActiveLayer();
|
||||||
const isSpecialLayer = !!layer?.specialType;
|
const isSpecialLayer = !!layer?.isPrintTrims || !!layer?.isPrintTrimsGroup;
|
||||||
if (isSpecialLayer) {
|
if (isSpecialLayer) {
|
||||||
this._disableBrushIndicator();
|
this._disableBrushIndicator();
|
||||||
this.canvas.defaultCursor = "not-allowed";
|
this.canvas.defaultCursor = "not-allowed";
|
||||||
@@ -696,7 +695,7 @@ export class ToolManager {
|
|||||||
|
|
||||||
// // 设置矩形选区模式
|
// // 设置矩形选区模式
|
||||||
// // 这里需要具体的矩形选区工具实现
|
// // 这里需要具体的矩形选区工具实现
|
||||||
console.log("矩形选区工具已激活");
|
// console.log("矩形选区工具已激活");
|
||||||
|
|
||||||
if (this.canvasManager && this.canvasManager.selectionManager) {
|
if (this.canvasManager && this.canvasManager.selectionManager) {
|
||||||
this.canvasManager.selectionManager.setCurrentTool(
|
this.canvasManager.selectionManager.setCurrentTool(
|
||||||
@@ -705,6 +704,77 @@ export class ToolManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置部件选取工具
|
||||||
|
*/
|
||||||
|
setupPartTool(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 设置部件选取工具--矩形
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置波浪工具
|
* 设置波浪工具
|
||||||
*/
|
*/
|
||||||
@@ -848,7 +918,7 @@ export class ToolManager {
|
|||||||
this._rasterizeLayerForLiquify(layerId);
|
this._rasterizeLayerForLiquify(layerId);
|
||||||
},
|
},
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
console.log("用户取消了栅格化操作");
|
// console.log("用户取消了栅格化操作");
|
||||||
// 用户取消,触发液化面板显示事件但不能液化
|
// 用户取消,触发液化面板显示事件但不能液化
|
||||||
document.dispatchEvent(
|
document.dispatchEvent(
|
||||||
new CustomEvent("showLiquifyPanel", {
|
new CustomEvent("showLiquifyPanel", {
|
||||||
@@ -1240,7 +1310,7 @@ export class ToolManager {
|
|||||||
*/
|
*/
|
||||||
showTextEditor(textObject, layer) {
|
showTextEditor(textObject, layer) {
|
||||||
// 这个方法将在TextEditorPanel组件实现后调用
|
// 这个方法将在TextEditorPanel组件实现后调用
|
||||||
console.log("显示文本编辑面板", textObject, layer);
|
// console.log("显示文本编辑面板", textObject, layer);
|
||||||
// 将发出一个事件,让Vue组件捕获并显示编辑面板
|
// 将发出一个事件,让Vue组件捕获并显示编辑面板
|
||||||
document.dispatchEvent(
|
document.dispatchEvent(
|
||||||
new CustomEvent("showTextEditor", {
|
new CustomEvent("showTextEditor", {
|
||||||
@@ -1256,7 +1326,7 @@ export class ToolManager {
|
|||||||
*/
|
*/
|
||||||
hideTextEditor() {
|
hideTextEditor() {
|
||||||
// 这个方法将在TextEditorPanel组件实现后调用
|
// 这个方法将在TextEditorPanel组件实现后调用
|
||||||
console.log("隐藏文本编辑面板");
|
// console.log("隐藏文本编辑面板");
|
||||||
// 将发出一个事件,让Vue组件捕获并隐藏编辑面板
|
// 将发出一个事件,让Vue组件捕获并隐藏编辑面板
|
||||||
document.dispatchEvent(
|
document.dispatchEvent(
|
||||||
new CustomEvent("hideTextEditor", {
|
new CustomEvent("hideTextEditor", {
|
||||||
@@ -1277,7 +1347,7 @@ export class ToolManager {
|
|||||||
if (this.brushIndicator) {
|
if (this.brushIndicator) {
|
||||||
this.brushIndicator.dispose();
|
this.brushIndicator.dispose();
|
||||||
this.brushIndicator = null;
|
this.brushIndicator = null;
|
||||||
console.log("笔刷指示器已清理");
|
// console.log("笔刷指示器已清理");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除文本编辑相关事件监听器
|
// 移除文本编辑相关事件监听器
|
||||||
@@ -1302,7 +1372,7 @@ export class ToolManager {
|
|||||||
this.canvas.isDrawingMode = false;
|
this.canvas.isDrawingMode = false;
|
||||||
this.canvas.selection = false;
|
this.canvas.selection = false;
|
||||||
|
|
||||||
console.log("文本工具已激活");
|
// console.log("文本工具已激活");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1378,7 +1448,7 @@ export class ToolManager {
|
|||||||
// 切换到红色笔刷工具作为默认工具
|
// 切换到红色笔刷工具作为默认工具
|
||||||
this.setTool(OperationType.RED_BRUSH);
|
this.setTool(OperationType.RED_BRUSH);
|
||||||
|
|
||||||
console.log("工具管理器已进入红绿图模式");
|
// console.log("工具管理器已进入红绿图模式");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1391,7 +1461,7 @@ export class ToolManager {
|
|||||||
// 切换回选择工具
|
// 切换回选择工具
|
||||||
this.setTool(OperationType.SELECT);
|
this.setTool(OperationType.SELECT);
|
||||||
|
|
||||||
console.log("工具管理器已退出红绿图模式");
|
// console.log("工具管理器已退出红绿图模式");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1449,7 +1519,7 @@ export class ToolManager {
|
|||||||
// 更新指示器颜色
|
// 更新指示器颜色
|
||||||
this.brushIndicator.updateColor(brushColor);
|
this.brushIndicator.updateColor(brushColor);
|
||||||
|
|
||||||
console.log(`笔刷指示器已启用,大小: ${brushSize}, 颜色: ${brushColor}`);
|
// console.log(`笔刷指示器已启用,大小: ${brushSize}, 颜色: ${brushColor}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1460,7 +1530,7 @@ export class ToolManager {
|
|||||||
if (!this.brushIndicator) return;
|
if (!this.brushIndicator) return;
|
||||||
|
|
||||||
this.brushIndicator.disable();
|
this.brushIndicator.disable();
|
||||||
console.log("笔刷指示器已禁用");
|
// console.log("笔刷指示器已禁用");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1471,7 +1541,7 @@ export class ToolManager {
|
|||||||
if (!this.brushIndicator) return;
|
if (!this.brushIndicator) return;
|
||||||
|
|
||||||
this.brushIndicator.updateSize(size);
|
this.brushIndicator.updateSize(size);
|
||||||
console.log(`笔刷指示器大小已更新为: ${size}`);
|
// console.log(`笔刷指示器大小已更新为: ${size}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1482,7 +1552,7 @@ export class ToolManager {
|
|||||||
if (!this.brushIndicator) return;
|
if (!this.brushIndicator) return;
|
||||||
|
|
||||||
this.brushIndicator.updateColor(color);
|
this.brushIndicator.updateColor(color);
|
||||||
console.log(`笔刷指示器颜色已更新为: ${color}`);
|
// console.log(`笔刷指示器颜色已更新为: ${color}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1558,6 +1628,8 @@ export class ToolManager {
|
|||||||
OperationType.RED_BRUSH,
|
OperationType.RED_BRUSH,
|
||||||
OperationType.GREEN_BRUSH,
|
OperationType.GREEN_BRUSH,
|
||||||
OperationType.LIQUIFY,
|
OperationType.LIQUIFY,
|
||||||
|
OperationType.PART_BRUSH,
|
||||||
|
OperationType.PART_ERASER,
|
||||||
];
|
];
|
||||||
|
|
||||||
return brushTools.includes(currentTool);
|
return brushTools.includes(currentTool);
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ export class CommandManager {
|
|||||||
this.undoStack = [];
|
this.undoStack = [];
|
||||||
this.redoStack = [];
|
this.redoStack = [];
|
||||||
this._notifyStateChange();
|
this._notifyStateChange();
|
||||||
console.log("📝 命令历史已清空");
|
// console.log("📝 命令历史已清空");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||