Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite
260
package-lock.json
generated
@@ -34,6 +34,7 @@
|
||||
"vue-draggable-plus": "^0.6.0",
|
||||
"vue-i18n": "^9.6.1",
|
||||
"vue-router": "^4.0.3",
|
||||
"vue3-moveable": "^0.28.0",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"vuex": "^4.0.0",
|
||||
"x-sender": "^1.1.6"
|
||||
@@ -232,6 +233,15 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cfcs/core": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/@cfcs/core/-/core-0.0.6.tgz",
|
||||
"integrity": "sha512-FxfJMwoLB8MEMConeXUCqtMGqxdtePQxRBOiGip9ULcYYam3WfCgoY6xdnMaSkYvRvmosp5iuG+TiPofm65+Pw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@egjs/component": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@ctrl/tinycolor": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
|
||||
@@ -240,6 +250,39 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@daybrush/utils": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmmirror.com/@daybrush/utils/-/utils-1.13.0.tgz",
|
||||
"integrity": "sha512-ALK12C6SQNNHw1enXK+UO8bdyQ+jaWNQ1Af7Z3FNxeAwjYhQT7do+TRE4RASAJ3ObaS2+TJ7TXR3oz2Gzbw0PQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@egjs/agent": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmmirror.com/@egjs/agent/-/agent-2.4.4.tgz",
|
||||
"integrity": "sha512-cvAPSlUILhBBOakn2krdPnOGv5hAZq92f1YHxYcfu0p7uarix2C6Ia3AVizpS1SGRZGiEkIS5E+IVTLg1I2Iog==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@egjs/children-differ": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/@egjs/children-differ/-/children-differ-1.0.1.tgz",
|
||||
"integrity": "sha512-DRvyqMf+CPCOzAopQKHtW+X8iN6Hy6SFol+/7zCUiE5y4P/OB8JP8FtU4NxtZwtafvSL4faD5KoQYPj3JHzPFQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@egjs/list-differ": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@egjs/component": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/@egjs/component/-/component-3.0.5.tgz",
|
||||
"integrity": "sha512-cLcGizTrrUNA2EYE3MBmEDt2tQv1joVP1Q3oDisZ5nw0MZDx2kcgEXM+/kZpfa/PAkFvYVhRUZwytIQWoN3V/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@egjs/list-differ": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/@egjs/list-differ/-/list-differ-1.0.1.tgz",
|
||||
"integrity": "sha512-OTFTDQcWS+1ZREOdCWuk5hCBgYO4OsD30lXcOCyVOAjXMhgL5rBRDnt/otb6Nz8CzU0L/igdcaQBDLWc4t9gvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@element-plus/icons-vue": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
|
||||
@@ -1224,6 +1267,34 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@scena/dragscroll": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/@scena/dragscroll/-/dragscroll-1.4.0.tgz",
|
||||
"integrity": "sha512-3O8daaZD9VXA9CP3dra6xcgt/qrm0mg0xJCwiX6druCteQ9FFsXffkF8PrqxY4Z4VJ58fFKEa0RlKqbsi/XnRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.6.0",
|
||||
"@scena/event-emitter": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@scena/event-emitter": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/@scena/event-emitter/-/event-emitter-1.0.5.tgz",
|
||||
"integrity": "sha512-AzY4OTb0+7ynefmWFQ6hxDdk0CySAq/D4efljfhtRHCOP7MBF9zUfhKG3TJiroVjASqVgkRJFdenS8ArZo6Olg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@scena/matrix": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/@scena/matrix/-/matrix-1.1.1.tgz",
|
||||
"integrity": "sha512-JVKBhN0tm2Srl+Yt+Ywqu0oLgLcdemDQlD1OxmN9jaCTwaFPZ7tY8n6dhVgMEaR9qcR7r+kAlMXnSfNyYdE+Vg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@simonwep/pickr": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmmirror.com/@simonwep/pickr/-/pickr-1.8.2.tgz",
|
||||
@@ -2904,6 +2975,52 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/croact": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/croact/-/croact-1.0.4.tgz",
|
||||
"integrity": "sha512-9GhvyzTY/IVUrMQ2iz/mzgZ8+NcjczmIo/t4FkC1CU0CEcau6v6VsEih4jkTa4ZmRgYTF0qXEZLObCzdDFplpw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.13.0",
|
||||
"@egjs/list-differ": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/croact-css-styled": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmmirror.com/croact-css-styled/-/croact-css-styled-1.1.9.tgz",
|
||||
"integrity": "sha512-G7yvRiVJ3Eoj0ov2h2xR4312hpOzATay2dGS9clK8yJQothjH1sBXIyvOeRP5wBKD9mPcKcoUXPCPsl0tQog4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.13.0",
|
||||
"css-styled": "~1.0.8",
|
||||
"framework-utils": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/croact-moveable": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmmirror.com/croact-moveable/-/croact-moveable-0.9.0.tgz",
|
||||
"integrity": "sha512-fc3bieV6CdqqZFtzsSLi9KmvUMFW3oakUfhPCls1BxKjOfUfn8rktteGED2341A/Qghy8tI3Hm6SdocIc68IKg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.13.0",
|
||||
"@egjs/agent": "^2.2.1",
|
||||
"@egjs/children-differ": "^1.0.1",
|
||||
"@egjs/list-differ": "^1.0.0",
|
||||
"@scena/dragscroll": "^1.4.0",
|
||||
"@scena/event-emitter": "^1.0.5",
|
||||
"@scena/matrix": "^1.1.1",
|
||||
"croact-css-styled": "^1.1.9",
|
||||
"css-to-mat": "^1.1.1",
|
||||
"framework-utils": "^1.1.0",
|
||||
"gesto": "^1.19.3",
|
||||
"overlap-area": "^1.1.0",
|
||||
"react-css-styled": "^1.1.9",
|
||||
"react-moveable": "~0.56.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"croact": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -3014,6 +3131,25 @@
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/css-styled": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/css-styled/-/css-styled-1.0.8.tgz",
|
||||
"integrity": "sha512-tCpP7kLRI8dI95rCh3Syl7I+v7PP+2JYOzWkl0bUEoSbJM+u8ITbutjlQVf0NC2/g4ULROJPi16sfwDIO8/84g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/css-to-mat": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/css-to-mat/-/css-to-mat-1.1.1.tgz",
|
||||
"integrity": "sha512-kvpxFYZb27jRd2vium35G7q5XZ2WJ9rWjDUMNT36M3Hc41qCrLXFM5iEKMGXcrPsKfXEN+8l/riB4QzwwwiEyQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.13.0",
|
||||
"@scena/matrix": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-1.1.3.tgz",
|
||||
@@ -4354,6 +4490,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/framework-utils": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/framework-utils/-/framework-utils-1.1.0.tgz",
|
||||
"integrity": "sha512-KAfqli5PwpFJ8o3psRNs8svpMGyCSAe8nmGcjQ0zZBWN2H6dZDnq+ABp3N3hdUmFeMrLtjOCTXD4yplUJIWceg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||
@@ -4485,6 +4627,16 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/gesto": {
|
||||
"version": "1.19.4",
|
||||
"resolved": "https://registry.npmmirror.com/gesto/-/gesto-1.19.4.tgz",
|
||||
"integrity": "sha512-hfr/0dWwh0Bnbb88s3QVJd1ZRJeOWcgHPPwmiH6NnafDYvhTsxg+SLYu+q/oPNh9JS3V+nlr6fNs8kvPAtcRDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.13.0",
|
||||
"@scena/event-emitter": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
@@ -5695,6 +5847,24 @@
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/keycode": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/keycode/-/keycode-2.2.1.tgz",
|
||||
"integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/keycon": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/keycon/-/keycon-1.4.0.tgz",
|
||||
"integrity": "sha512-p1NAIxiRMH3jYfTeXRs2uWbVJ1WpEjpi8ktzUyBJsX7/wn2qu2VRXktneBLNtKNxJmlUYxRi9gOJt1DuthXR7A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cfcs/core": "^0.0.6",
|
||||
"@daybrush/utils": "^1.7.1",
|
||||
"@scena/event-emitter": "^1.0.2",
|
||||
"keycode": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
|
||||
@@ -6212,6 +6382,19 @@
|
||||
"pathe": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/moveable": {
|
||||
"version": "0.53.0",
|
||||
"resolved": "https://registry.npmmirror.com/moveable/-/moveable-0.53.0.tgz",
|
||||
"integrity": "sha512-71jS9zIoQzMhnNvduhg4tUEdm23+fO/40FN7muVMbZvVwbTku2MIxxLhnU4qFvxI4oVxn75l79SbtgjuA+s7Pw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.13.0",
|
||||
"@scena/event-emitter": "^1.0.5",
|
||||
"croact": "^1.0.4",
|
||||
"croact-moveable": "~0.9.0",
|
||||
"react-moveable": "~0.56.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
|
||||
@@ -6650,6 +6833,15 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/overlap-area": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/overlap-area/-/overlap-area-1.1.0.tgz",
|
||||
"integrity": "sha512-3dlJgJCaVeXH0/eZjYVJvQiLVVrPO4U1ZGqlATtx6QGO3b5eNM6+JgUKa7oStBTdYuGTk7gVoABCW6Tp+dhRdw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.7.1"
|
||||
}
|
||||
},
|
||||
"node_modules/own-keys": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz",
|
||||
@@ -7037,6 +7229,46 @@
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-css-styled": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmmirror.com/react-css-styled/-/react-css-styled-1.1.9.tgz",
|
||||
"integrity": "sha512-M7fJZ3IWFaIHcZEkoFOnkjdiUFmwd8d+gTh2bpqMOcnxy/0Gsykw4dsL4QBiKsxcGow6tETUa4NAUcmJF+/nfw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"css-styled": "~1.0.8",
|
||||
"framework-utils": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-moveable": {
|
||||
"version": "0.56.0",
|
||||
"resolved": "https://registry.npmmirror.com/react-moveable/-/react-moveable-0.56.0.tgz",
|
||||
"integrity": "sha512-FmJNmIOsOA36mdxbrc/huiE4wuXSRlmon/o+/OrfNhSiYYYL0AV5oObtPluEhb2Yr/7EfYWBHTxF5aWAvjg1SA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.13.0",
|
||||
"@egjs/agent": "^2.2.1",
|
||||
"@egjs/children-differ": "^1.0.1",
|
||||
"@egjs/list-differ": "^1.0.0",
|
||||
"@scena/dragscroll": "^1.4.0",
|
||||
"@scena/event-emitter": "^1.0.5",
|
||||
"@scena/matrix": "^1.1.1",
|
||||
"css-to-mat": "^1.1.1",
|
||||
"framework-utils": "^1.1.0",
|
||||
"gesto": "^1.19.3",
|
||||
"overlap-area": "^1.1.0",
|
||||
"react-css-styled": "^1.1.9",
|
||||
"react-selecto": "^1.25.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-selecto": {
|
||||
"version": "1.26.3",
|
||||
"resolved": "https://registry.npmmirror.com/react-selecto/-/react-selecto-1.26.3.tgz",
|
||||
"integrity": "sha512-Ubik7kWSnZyQEBNro+1k38hZaI1tJarE+5aD/qsqCOA1uUBSjgKVBy3EWRzGIbdmVex7DcxznFZLec/6KZNvwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"selecto": "~1.26.3"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
@@ -7507,6 +7739,24 @@
|
||||
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/selecto": {
|
||||
"version": "1.26.3",
|
||||
"resolved": "https://registry.npmmirror.com/selecto/-/selecto-1.26.3.tgz",
|
||||
"integrity": "sha512-gZHgqMy5uyB6/2YDjv3Qqaf7bd2hTDOpPdxXlrez4R3/L0GiEWDCFaUfrflomgqdb3SxHF2IXY0Jw0EamZi7cw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.13.0",
|
||||
"@egjs/children-differ": "^1.0.1",
|
||||
"@scena/dragscroll": "^1.4.0",
|
||||
"@scena/event-emitter": "^1.0.5",
|
||||
"css-styled": "^1.0.8",
|
||||
"css-to-mat": "^1.1.1",
|
||||
"framework-utils": "^1.1.0",
|
||||
"gesto": "^1.19.4",
|
||||
"keycon": "^1.2.0",
|
||||
"overlap-area": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz",
|
||||
@@ -9765,6 +10015,16 @@
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue3-moveable": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/vue3-moveable/-/vue3-moveable-0.28.0.tgz",
|
||||
"integrity": "sha512-vplQO0XkxVEtXMDh2/lZE+c5kMycGXAfYFMvbwFKi8UVYzVk8MTgVHr4fxO9Z+4i4Rb+U/IEIgkhHRMAbx8FJg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"framework-utils": "^1.1.0",
|
||||
"moveable": "~0.53.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vuedraggable": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"vue-draggable-plus": "^0.6.0",
|
||||
"vue-i18n": "^9.6.1",
|
||||
"vue-router": "^4.0.3",
|
||||
"vue3-moveable": "^0.28.0",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"vuex": "^4.0.0",
|
||||
"x-sender": "^1.1.6"
|
||||
|
||||
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 |
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 |
68
src/assets/icons/CPart.svg
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="103.000000pt" height="92.000000pt" viewBox="0 0 103.000000 92.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
|
||||
<g transform="translate(0.000000,92.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M365 895 c-5 -2 -36 -6 -67 -10 -45 -5 -58 -10 -59 -23 0 -11 -2 -12
|
||||
-6 -4 -7 17 -32 15 -40 -4 -4 -11 -8 -12 -13 -4 -5 8 -13 9 -21 4 -7 -4 -22
|
||||
-9 -34 -10 -11 -1 -41 -11 -66 -21 l-47 -18 20 -63 c11 -38 23 -60 30 -56 6 4
|
||||
8 -1 3 -15 -3 -11 -3 -21 2 -21 4 0 9 -12 9 -26 1 -14 5 -28 8 -32 4 -3 33 3
|
||||
66 15 33 11 61 19 62 18 2 -2 1 -139 -2 -304 l-5 -301 309 2 309 3 -2 108 c-2
|
||||
77 2 113 11 125 10 12 10 14 1 8 -7 -4 -13 -2 -13 4 0 6 10 8 23 5 18 -5 26
|
||||
-1 36 17 9 17 10 18 6 3 -5 -16 -4 -18 6 -7 18 17 -1 34 -40 37 l-32 2 -2 151
|
||||
c-1 82 0 147 3 144 9 -12 44 -11 52 1 5 8 8 7 8 -4 0 -14 26 -28 52 -29 11 0
|
||||
39 70 33 80 -3 5 1 11 7 13 10 4 9 8 -2 16 -13 10 -8 15 12 12 8 -1 38 91 32
|
||||
98 -2 2 -10 -2 -18 -8 -9 -7 -17 -8 -20 -2 -3 5 0 11 6 14 7 2 -18 14 -56 26
|
||||
-38 12 -71 19 -74 16 -3 -3 -11 0 -18 6 -8 6 -20 9 -28 6 -8 -3 -16 -2 -18 3
|
||||
-5 15 -154 30 -286 29 -70 0 -131 -2 -137 -4z m21 -30 c-6 -18 3 -47 14 -40 4
|
||||
2 18 -7 30 -20 27 -29 17 -34 -14 -7 -20 16 -20 16 -7 -1 23 -29 66 -47 112
|
||||
-47 34 0 40 3 35 16 -5 14 -4 15 9 4 13 -11 19 -9 36 6 12 11 29 35 38 54 15
|
||||
31 20 35 58 35 24 0 43 -2 43 -5 0 -11 115 -27 121 -18 3 5 9 2 13 -7 4 -13
|
||||
14 -16 34 -12 15 2 36 1 47 -4 16 -7 14 -8 -10 -5 l-30 5 32 -14 c39 -18 39
|
||||
-19 14 -83 -15 -38 -17 -52 -8 -61 9 -9 8 -11 -7 -5 -16 6 -18 4 -12 -19 3
|
||||
-14 2 -29 -4 -32 -6 -3 -7 1 -4 9 6 16 -9 20 -73 19 -12 0 -20 4 -17 8 3 5 -4
|
||||
9 -15 9 -20 0 -21 -5 -21 -151 l0 -151 -27 8 c-16 4 -38 7 -50 7 -19 -1 -21 2
|
||||
-13 17 15 28 12 57 -6 57 -11 0 -15 -8 -12 -27 4 -24 1 -27 -23 -26 -15 1 -25
|
||||
4 -22 9 2 4 -2 7 -10 7 -9 0 -14 -10 -14 -25 0 -13 3 -22 8 -19 5 3 6 -1 3 -9
|
||||
-2 -7 0 -25 5 -40 8 -20 7 -25 -3 -21 -7 3 -13 -2 -13 -11 0 -12 7 -15 26 -11
|
||||
14 3 21 3 14 0 -7 -3 -9 -12 -6 -20 3 -7 10 -11 16 -7 5 3 7 1 4 -4 -9 -14 3
|
||||
-74 12 -68 4 2 7 -6 7 -18 -1 -32 32 -34 44 -3 5 14 7 34 5 46 -3 13 0 18 7
|
||||
14 8 -5 9 -1 5 10 -4 9 -3 15 2 12 5 -3 12 1 15 10 3 8 2 12 -4 9 -6 -3 -10
|
||||
-1 -10 4 0 13 3 13 24 5 13 -5 16 -24 16 -103 0 -63 4 -102 13 -111 10 -12 9
|
||||
-12 -4 -2 -13 10 -88 12 -300 10 l-284 -3 3 303 3 302 -26 0 c-14 0 -25 -4
|
||||
-25 -10 0 -5 -7 -6 -17 -3 -9 4 -14 2 -10 -3 4 -6 -6 -9 -24 -6 -28 4 -41 -11
|
||||
-19 -21 6 -3 5 -4 -2 -3 -7 2 -13 11 -13 22 -1 32 -25 104 -35 104 -5 0 -6 7
|
||||
-3 17 4 10 2 14 -5 9 -7 -4 -10 2 -8 17 3 29 14 47 29 47 7 0 3 -8 -8 -17
|
||||
l-20 -16 20 8 c11 4 28 10 37 12 10 3 18 9 18 13 0 4 6 7 12 7 22 -2 158 26
|
||||
158 32 0 3 20 6 45 5 25 0 45 3 45 8 0 4 3 8 6 8 3 0 4 -7 0 -15z m237 -5 c-3
|
||||
-9 1 -8 11 4 9 11 16 15 16 9 0 -6 -7 -16 -15 -23 -8 -7 -15 -9 -15 -4 0 10
|
||||
-29 -28 -30 -40 0 -4 8 -5 17 -2 15 6 15 4 -2 -14 -22 -24 -37 -26 -28 -4 5
|
||||
14 3 15 -15 5 -26 -14 -77 -14 -103 -1 -10 6 -27 27 -38 48 l-19 37 113 0 c95
|
||||
0 112 -2 108 -15z"/>
|
||||
<path d="M346 641 c-3 -5 1 -12 10 -15 23 -9 36 -7 29 4 -3 6 1 7 9 4 9 -3 16
|
||||
-1 16 5 0 13 -56 15 -64 2z"/>
|
||||
<path d="M440 640 c0 -16 33 -26 38 -12 2 7 8 10 13 6 5 -3 9 0 9 5 0 6 -13
|
||||
11 -30 11 -16 0 -30 -5 -30 -10z"/>
|
||||
<path d="M530 641 c0 -12 37 -24 50 -16 20 12 10 25 -20 25 -16 0 -30 -4 -30
|
||||
-9z"/>
|
||||
<path d="M620 641 c0 -12 37 -24 50 -16 20 12 10 25 -20 25 -16 0 -30 -4 -30
|
||||
-9z"/>
|
||||
<path d="M310 593 c0 -20 5 -30 16 -30 10 0 14 8 12 25 -4 37 -28 40 -28 5z"/>
|
||||
<path d="M697 613 c-13 -13 -7 -50 8 -50 10 0 15 10 15 29 0 27 -9 35 -23 21z"/>
|
||||
<path d="M317 534 c-4 -4 -7 -20 -7 -36 0 -35 23 -34 28 1 4 25 -10 46 -21 35z"/>
|
||||
<path d="M692 503 c4 -39 28 -42 28 -4 0 21 -5 31 -16 31 -11 0 -14 -8 -12
|
||||
-27z"/>
|
||||
<path d="M312 415 c4 -33 22 -33 26 0 2 18 -1 25 -13 25 -12 0 -15 -7 -13 -25z"/>
|
||||
<path d="M312 329 c2 -19 8 -33 13 -31 15 3 12 55 -3 60 -10 3 -13 -5 -10 -29z"/>
|
||||
<path d="M342 278 c3 -7 19 -14 37 -16 24 -3 32 0 29 10 -3 7 -19 14 -37 16
|
||||
-24 3 -32 0 -29 -10z"/>
|
||||
<path d="M443 275 c0 -10 10 -15 29 -15 18 0 28 5 28 15 0 10 -10 15 -28 15
|
||||
-19 0 -29 -5 -29 -15z"/>
|
||||
<path d="M530 275 c0 -10 10 -15 33 -15 22 0 28 3 18 9 -11 7 -11 9 0 14 8 3
|
||||
-1 6 -18 6 -23 1 -33 -4 -33 -14z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 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 |
9
src/assets/icons/overallMore.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<rect width="24" height="24" fill="url(#pattern0_2641_12790)"/>
|
||||
<defs>
|
||||
<pattern id="pattern0_2641_12790" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image0_2641_12790" transform="scale(0.0078125)"/>
|
||||
</pattern>
|
||||
<image id="image0_2641_12790" width="128" height="128" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAACIlJREFUeJztnWlsFVUUgL9OyxNwoVUoCBZFVNxQUaNWo0aiica4SxTUGJe4x+0PBJc/rlH8oSYucQvgQkARixpCECNqgtGKiiigssbK5sLeCvT54/TR+mz75s7cuXfeeL7k/Gpn3jlzZ+5y7rnngKIo/18qfCuQcfYGBgF922QzsA5YD6zxqJeSEBXAWcAEoBHYBeS7kFXAROAaoLcPZRV75IAbgYV03eDdye/AY8BA14or8akHFhGt4YtlKzAWqHRqgRKJAHiC7rv5qPIZsL87UxRT9gCmYL/hO8py4HBXBinhyQFzSbbxC7IBONKNWUpYXsRN43fsCfo6sUwpya24bfyCzEZ9Nt7pD2zEzwuQB65N3kSlOybhr/HziOewj22jdL0ZjiOQsd+0G94MvAe8grxAHwM/A7VAjeG99gK2AJ8aXqdYYAJmX+tO4HGguov7VQBjgCbD+y5D/A+KQ3oAawnfSFuBc0PeeyCwwODeeeAcCzZ1yjHAm8hY43OsK3cZZfjcB2LeE0SV34A3gOHFSowBWlLw8Mpdphc/2JBc5VjPFmB04cePQRvflpxENAJghWNdW4DhATAOcW8q8WgCvox4bSsw06IuYcgB4wJgpOMfzipLkS8rKottKWLAyICulyqKGZtjXr/RihZmVBfGHiU+tTGvH2BFCzOWB8BUDz+cRYYTL7bvFFuKGDANxL/8M/5n0FmQ3UsrQ2qQIcSlrj8B+xQUGIK5R0rlv7KEaCuqpxzr2Yi0+b82NyqBC5CwZo1HaycHXGTw/68gEcJhOR9oILyPvxHZE4jCb8iGVAOy9FRC0ojZF/Yy4XqC0cA2g/u2AodYskkx4DbMu9mliIu9eGIYIBO+hgj3nJuQfUoJqoE/iDbebgPmI3EB85BuOOrYfWnShipdcyd+JpYF+RSNC/RKFfAdfhp/J3Bs8iYqpTgWCcty/QKMdWGcEo5LkNm4q8afinb9qeMukjkTWCwfAr0c2aQYcimwneQafxISj6ikmFOBH7Hb8FuQHka7/TKhJ/AwdnqDD4CDnGqvWKMfMltfjVmjtyATvZPdq6zdTBJUAscjm2qnA4ORl6MvsAlJELUW+ArZmJlH/GgiRVEURVEURQlPGlYBVciRqhOR2XItkmJ1AzJjXo1shf7iS0HFPgESZ/c+4aNhVwLPoVmzypoAuIl4YeitwCykx1DKiKHAR9jzme9C0rbs5dIIJRqXYRb9aiKLEG+bklLuIPm98ybgOFcGKeG5iWQbvqNsoO20i5IOTgWacfcC5IEf6HDeTfFHDf6STU12YJ9Sgmfx0/gFOTN5E5WuOBrYgd8X4Gs0oaI3XsO8wf4CngROQ1zBOaAOybk3nWhh2GGTNSoW2QfzgxNTgP1K3PckZC/A5L5v2zMruxRvBvVCXKxR89XUA/cY/P/Tbf+fD/G/fYFPCL8X8DdwHTIcKcIaJBRte/EfKoEHcZumZA7m2cqH4udoVpZkE/BAx2dfAbzlWIlddJKvNiQPpeAhZkHepG0EcJ2nNk+8nPd1uD2bl2UZEwA3mz1/K8yJce1q/GTVzCI3B8AIDz+8Mub1q6xooYzw5SyJ+7tpCGXLAvkAyQ/omjrP1yvCggB43sMPxyl7MhgtqWqLF0C60zdwO/vchRSqiMLDjnXNqrxOh6G0ErgfcRK4UmAu5o6gQ5GiTL4fXjnLRmB84dkXT6Z6Iq7gqKliTV3BzwB3tylWCnUFxyNPuyu4OakfibIZNJXSxZHrkfy4JvedZs8sxYRXidYtTUDO0/dHtoMHA1cAM9Dt4LLiKPwHhDSiASFeeRp/jd8KnJG8iUp31BAvKXIcmejAPiUE9bgPC/8G2NOFcUo4bsBd469DU6ylkutJflL4K5pNO9VcTHKHQxcCB7gzRYnKwUggiK2G34GsNnTMLyMqkCGhUGc3irQiKVV9BLIolgiQEnUNhN+AWo4cORvmQd/MkobImipkA+pE5FRQLbKnsL5NVgKfIS+AoiiKoiiKoihxScMqIGtUASfQXjCiDol46kd7wYh1SGjWXKRgxCYvmipWqSVeyZh69yorNugJPIadre9ZiMtcKRNOA5Zgd3NrKxJZrcNzyrmcZAtHTkaCY5UUci9uchTMBno7skkJyWW4TVDxDjocpIbj8JOjaLwL45TuqQK+w33j55HDtFogwzOFdHa+5HMSGApMT+f+X6kG3kXW/KZsQ04qNSIewB5IUSxT6oDvkYzoimNux/yLXQxcyX9n8RVI5tMZEe75cUL2KSX4GrOGeolwa/hRmOU7aEVyJFij45hSiZRyOws5oasIOeS5hOUlpFJKWM4DZhJ+OG5EjspHYS2yAdWATCx3MwQ5VuVzkpMFWUw0792TjvVcQIfyOtWYZ+JW6VyuJBo1uM3TnEdqN/Yp5AYy6eKUztkG3Ei0lDTNwPG4rYq6L7AjQDY1lPgsRF6CqHxhSxEDRgXoiVpbrI15/RorWpgxJEDKtSjxiVuuro8VLcz4M0Dq+SrxGUY8V62P7KcfgRRuaMH/DDoLcrLR428nAFY41rUZqfAGwGj0JbAhM4jG1Y71bKaTJetwJGdwk6eHlxW5ovjBlmAQ7hJqNSE5gnd/+UppeiAz/LAPeSvi3g3DIMw9sGdbsEkxZAJmjbQTce/WdHG/AOn2Tb/8ZVhOgKmxZuE4AliE+fPagsT6z0fW+TXAYcCFwIER9LgPeDTCdYoFJuJ3brGG+L4GJQb9EaeZrxfgmuRNVEpxC34afxY6XKeGF3Db+MsoXU9BcUgOu/kOu5N1yARUSRk5kq+3vBxNiZdqAuBxZM1vu/HnAQPcmaLEYQTmUcNdyRYkyYRWOykzckiFsm+J1vDrgUfQrz4TnAk8AXxJ98PDCuA14Cqglw9FQdeWSbMnksq+H7KU24TM7AuiKIrikX8A+4ThOTuVZbQAAAAASUVORK5CYII="/>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 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 |
BIN
src/assets/images/icon/xyz.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
@@ -7,6 +7,7 @@ import {
|
||||
insertObjectAtZIndex,
|
||||
removeCanvasObjectByObject,
|
||||
createPatternTransform,
|
||||
imageAddGapToCanvas,
|
||||
} from "../utils/helper";
|
||||
import { restoreFabricObject } from "../utils/objectHelper";
|
||||
|
||||
@@ -308,16 +309,8 @@ export class FillRepeatGapChangeCommand extends Command {
|
||||
await image.decode();
|
||||
object.fill_.width = image.width;
|
||||
object.fill_.height = image.height;
|
||||
// 创建透明 Canvas
|
||||
const tcanvas = document.createElement('canvas');
|
||||
tcanvas.width = image.width + object.fill_.gapX;
|
||||
tcanvas.height = image.height + object.fill_.gapY;
|
||||
const ctx = tcanvas.getContext('2d');
|
||||
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
|
||||
ctx.drawImage(image, 0, 0);
|
||||
|
||||
const fill = object.get("fill");
|
||||
fill.source = tcanvas;
|
||||
fill.source = imageAddGapToCanvas(image, object.fill_.gapX, object.fill_.gapY);
|
||||
object.set("fill", new fabric.Pattern(fill));
|
||||
this.canvas.renderAll();
|
||||
return true;
|
||||
|
||||
@@ -872,13 +872,13 @@ export class ToggleChildLayerVisibilityCommand extends Command {
|
||||
// this.oldVisibility = this.childLayer ? this.childLayer.visible : null;
|
||||
}
|
||||
|
||||
async execute() {
|
||||
async execute(visible) {
|
||||
if (!this.childLayer) {
|
||||
throw new Error("找不到要切换可见性的子图层");
|
||||
}
|
||||
|
||||
// 切换可见性
|
||||
this.childLayer.visible = !this.childLayer.visible;
|
||||
this.childLayer.visible = typeof visible === "boolean" ? visible : !this.childLayer.visible;
|
||||
|
||||
// 更新画布上图层对象的可见性
|
||||
if (this.canvas) {
|
||||
|
||||
@@ -287,7 +287,7 @@ const canDeleteComputed = computed(() => {
|
||||
:is-child="isChild"
|
||||
:is-active="layer.id === activeLayerId"
|
||||
: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"
|
||||
:editing-name="editingLayerName"
|
||||
:can-delete="
|
||||
@@ -296,7 +296,7 @@ const canDeleteComputed = computed(() => {
|
||||
:expanded-group-ids="expandedGroupIds"
|
||||
@click="(...args) => forwardEvent('layer-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)"
|
||||
@toggle-visibility="(...args) => forwardEvent('toggle-visibility', ...args)"
|
||||
@toggle-lock="(...args) => forwardEvent('toggle-lock', ...args)"
|
||||
@@ -337,7 +337,7 @@ const canDeleteComputed = computed(() => {
|
||||
:expanded-group-ids="expandedGroupIds"
|
||||
:isChild="true"
|
||||
:parentLayerId="layer.id"
|
||||
:group-name="layer.specialType || groupName"
|
||||
:group-name="groupName"
|
||||
@layer-click="(...args) => forwardEvent('layer-click', ...args)"
|
||||
@layer-double-click="(...args) => forwardEvent('layer-double-click', ...args)"
|
||||
@context-menu="(...args) => forwardEvent('context-menu', ...args)"
|
||||
|
||||
@@ -1242,7 +1242,7 @@ async function handleCrossLevelMove(moveData) {
|
||||
try {
|
||||
const layer = findLayerRecursively(layers.value, layerId).layer;
|
||||
const toLayer = findLayerRecursively(layers.value, toParentId).layer;
|
||||
if(layer?.specialType || toLayer?.specialType) {
|
||||
if(layer?.isPrintTrims || layer?.isPrintTrimsGroup || toLayer?.isPrintTrims || toLayer?.isPrintTrimsGroup) {
|
||||
console.warn("当前图层不可移动到外部");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,479 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div
|
||||
class="part-selector-toolbar"
|
||||
v-if="visible"
|
||||
:class="{ active: !closePanel }"
|
||||
>
|
||||
<div class="btn" @click="setClosePanel">
|
||||
<i class="fi fi-br-angle-left"></i>
|
||||
</div>
|
||||
<!-- 顶部选区类型工具栏 -->
|
||||
<div class="toolbar-section">
|
||||
<div class="toolbar-header">
|
||||
<div class="header-title">
|
||||
{{ t("Canvas.GarmentPartSelector") }}
|
||||
</div>
|
||||
<!-- 移除关闭按钮,完全通过工具切换控制显示隐藏 -->
|
||||
<div class="tip">
|
||||
<div>
|
||||
<img
|
||||
src="/src/assets/images/canvas/shubiao-l.png"
|
||||
/>
|
||||
<span>Left Click: Add</span>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
src="/src/assets/images/canvas/shubiao-r.png"
|
||||
/>
|
||||
<span>Right Click: Remove</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tool-types">
|
||||
<div
|
||||
v-for="item in toolList"
|
||||
:key="item.type"
|
||||
:class="[
|
||||
'tool-btn',
|
||||
{ active: toolType === item.type },
|
||||
]"
|
||||
@click="setPartType(item.type)"
|
||||
>
|
||||
<svg-icon :name="item.icon" :size="item.size" />
|
||||
<span>{{ item.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<div class="toolbar-divider"></div>
|
||||
|
||||
<!-- 底部选区操作工具栏 -->
|
||||
<div class="tool-actions">
|
||||
<div class="action-btn" @click="onCreate">
|
||||
<svg-icon name="CPaste" size="16" />
|
||||
<span class="btn-text">{{
|
||||
$t("Canvas.creation")
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="action-btn" @click="onCopyCreate">
|
||||
<svg-icon name="CCut" size="26" />
|
||||
<span class="btn-text">{{
|
||||
$t("Canvas.CreateAndCopy")
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {
|
||||
CreateSelectionCommand,
|
||||
InvertSelectionCommand,
|
||||
FeatherSelectionCommand,
|
||||
FillSelectionCommand,
|
||||
} from "../commands/SelectionCommands";
|
||||
import { ToolCommand } from "../commands/ToolCommands";
|
||||
import {
|
||||
LassoCutoutCommand,
|
||||
ClearSelectionCommand,
|
||||
// CutSelectionToNewLayerCommand,
|
||||
} from "../commands/LassoCutoutCommand";
|
||||
|
||||
import { OperationType } from "../utils/layerHelper";
|
||||
import { ClearSelectionContentCommand } from "../commands/ClearSelectionContentCommand";
|
||||
import { CutSelectionToNewLayerCommand } from "../commands/CutSelectionToNewLayerCommand";
|
||||
|
||||
const props = defineProps({
|
||||
canvas: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
commandManager: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
partManager: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
partManager: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
layerManager: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
toolManager: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
activeTool: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
// 响应式数据
|
||||
const visible = ref(false);
|
||||
const toolType = ref(OperationType.PART);
|
||||
//打开隐藏操作面板
|
||||
const closePanel = ref(false);
|
||||
const setClosePanel = () => {
|
||||
closePanel.value = !closePanel.value;
|
||||
};
|
||||
|
||||
const toolList = [
|
||||
{
|
||||
type: OperationType.PART,
|
||||
label: "Point Selection",
|
||||
icon: "CPoint",
|
||||
size: "20",
|
||||
},
|
||||
{
|
||||
type: OperationType.PART_RECTANGLE,
|
||||
label: "Marquee Selection",
|
||||
icon: "CMarquee",
|
||||
size: "20",
|
||||
},
|
||||
{
|
||||
type: OperationType.PART_BRUSH,
|
||||
label: "Brush Selection",
|
||||
icon: "CBrush2",
|
||||
size: "16",
|
||||
},
|
||||
{
|
||||
type: OperationType.PART_ERASER,
|
||||
label: "Erase",
|
||||
icon: "CEraser2",
|
||||
size: "22",
|
||||
},
|
||||
];
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n();
|
||||
|
||||
onMounted(() => {});
|
||||
|
||||
// 监听 activeTool 变化
|
||||
watch(
|
||||
() => props.activeTool,
|
||||
(newTool) => {
|
||||
// 当工具为LASSO或AREA类型时显示选区面板
|
||||
const selectionTools = [
|
||||
OperationType.PART,
|
||||
OperationType.PART_RECTANGLE,
|
||||
OperationType.PART_BRUSH,
|
||||
OperationType.PART_ERASER,
|
||||
];
|
||||
|
||||
if (selectionTools.includes(newTool)) {
|
||||
show();
|
||||
// 根据工具类型设置选区类型
|
||||
toolType.value = newTool;
|
||||
|
||||
// 更新选区管理器的选区类型
|
||||
// if (props.partManager) {
|
||||
// props.partManager.setPartType(toolType.value);
|
||||
// props.partManager.setupPartEvents();
|
||||
// }
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/**
|
||||
* 显示面板
|
||||
*/
|
||||
function show() {
|
||||
visible.value = true;
|
||||
closePanel.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭面板
|
||||
*/
|
||||
function close() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选区类型
|
||||
*/
|
||||
function setPartType(type) {
|
||||
toolType.value = type;
|
||||
|
||||
// 通过 ToolManager 切换工具,这会自动通知 partManager
|
||||
if (props.toolManager) {
|
||||
props.toolManager.setToolWithCommand(type);
|
||||
}
|
||||
|
||||
// // 备用方案:如果没有 toolManager,直接更新 partManager
|
||||
// else if (props.partManager) {
|
||||
// props.partManager.setPartType(type);
|
||||
// props.partManager.setupPartEvents();
|
||||
// }
|
||||
}
|
||||
|
||||
// 创建
|
||||
function onCreate() {
|
||||
|
||||
}
|
||||
// 复制并创建
|
||||
function onCopyCreate() {
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.part-selector-toolbar {
|
||||
position: absolute;
|
||||
bottom: 22px;
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
max-width: min(90vw, 700px);
|
||||
margin: 0 auto;
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(15px);
|
||||
-webkit-backdrop-filter: blur(15px);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1000;
|
||||
color: #333;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
user-select: none;
|
||||
&.active {
|
||||
transform: translateY(100%);
|
||||
> .btn {
|
||||
> i {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
> .btn {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 22px;
|
||||
|
||||
> i {
|
||||
font-size: 1.4rem;
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 平板和手机适配 */
|
||||
@media screen and (max-width: 768px) {
|
||||
.part-selector-toolbar {
|
||||
bottom: 15px;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
max-width: calc(100vw - 30px);
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.part-selector-toolbar {
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
max-width: calc(100vw - 20px);
|
||||
}
|
||||
}
|
||||
|
||||
.part-selector-toolbar.is-active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.toolbar-header {
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
padding: 8px 0;
|
||||
// border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 8px 8px 0 0;
|
||||
position: relative;
|
||||
> .tip {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 10px;
|
||||
> img {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
> span {
|
||||
font-size: 10px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
text-align: left;
|
||||
}
|
||||
.header-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
padding: 3px 0;
|
||||
border-radius: 3px;
|
||||
transition: background-color 0.2s ease;
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
.header-btn:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.toolbar-section {
|
||||
padding: 0 3rem 1.2rem;
|
||||
}
|
||||
|
||||
.tool-types {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 8px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.tool-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 6px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tool-btn span {
|
||||
margin-top: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tool-btn svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.tool-btn:hover {
|
||||
background-color: rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.tool-btn.active {
|
||||
background-color: #007aff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.toolbar-divider {
|
||||
height: 1px;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.tool-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 5px;
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
/* 平板适配 - 每行4个按钮 */
|
||||
@media screen and (max-width: 768px) {
|
||||
.tool-actions {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px 6px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机适配 - 每行3个按钮 */
|
||||
@media screen and (max-width: 480px) {
|
||||
.tool-actions {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 6px 4px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.header-btn {
|
||||
font-size: 11px;
|
||||
padding: 2px 4px;
|
||||
min-width: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
display: flex;
|
||||
// flex-direction: column;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: none;
|
||||
border: none;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
gap: 4px;
|
||||
.c-svg {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn svg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
.action-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
@@ -1,14 +1,15 @@
|
||||
<template>
|
||||
<div class="repeat-setting">
|
||||
<div class="title">{{ t("Canvas.repeatSetting") }}</div>
|
||||
<div class="repeat-setting-item">
|
||||
<span class="label">{{ t("Canvas.angle") }}</span>
|
||||
<angle-tool
|
||||
:angle="angle"
|
||||
@input="(e) => emit('inputFillAngle', e)"
|
||||
@change="(e) => emit('changeFillAngle', e)"
|
||||
style-type="2"
|
||||
/>
|
||||
</div>
|
||||
<p></p>
|
||||
<div class="repeat-setting-item">
|
||||
<span class="label">{{ t("Canvas.scale") }}</span>
|
||||
<slider
|
||||
@@ -22,7 +23,6 @@
|
||||
@change="changeFillScale"
|
||||
/>
|
||||
</div>
|
||||
<p></p>
|
||||
<div class="repeat-setting-item">
|
||||
<span class="label">Gap X</span>
|
||||
<slider
|
||||
@@ -36,7 +36,6 @@
|
||||
@change="(e) => emit('changeFill_Gap', e, gapY)"
|
||||
/>
|
||||
</div>
|
||||
<p></p>
|
||||
<div class="repeat-setting-item">
|
||||
<span class="label">Gap Y</span>
|
||||
<slider
|
||||
@@ -50,14 +49,23 @@
|
||||
@change="(e) => emit('changeFill_Gap', gapX, e)"
|
||||
/>
|
||||
</div>
|
||||
<p></p>
|
||||
<div class="repeat-setting-item">
|
||||
<span class="label">{{ t("Canvas.offset") }}</span>
|
||||
<offset-tool
|
||||
:top="(props.object.fill?.offsetY / props.object.height) * 100"
|
||||
:left="(props.object.fill?.offsetX / props.object.width) * 100"
|
||||
:left="offsetX"
|
||||
:top="offsetY"
|
||||
@input="(e) => emit('inputFillOffset', e)"
|
||||
@change="(e) => emit('changeFillOffset', e)"
|
||||
:show-dish="false"
|
||||
/>
|
||||
</div>
|
||||
<div class="repeat-setting-item offset">
|
||||
<offset-tool
|
||||
:left="offsetX"
|
||||
:top="offsetY"
|
||||
@input="(e) => emit('inputFillOffset', e)"
|
||||
@change="(e) => emit('changeFillOffset', e)"
|
||||
:show-input="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -88,6 +96,12 @@
|
||||
});
|
||||
const gapX = computed(() => props.object.fill_?.gapX || 0);
|
||||
const gapY = computed(() => props.object.fill_?.gapY || 0);
|
||||
const offsetX = computed(
|
||||
() => (props.object.fill?.offsetX / props.object.width) * 100
|
||||
);
|
||||
const offsetY = computed(
|
||||
() => (props.object.fill?.offsetY / props.object.height) * 100
|
||||
);
|
||||
const emit = defineEmits([
|
||||
"inputFillAngle",
|
||||
"changeFillAngle",
|
||||
@@ -111,23 +125,36 @@
|
||||
<style scoped lang="less">
|
||||
.repeat-setting {
|
||||
user-select: none;
|
||||
width: 228px;
|
||||
> .title {
|
||||
line-height: 35px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin-top: -12px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
> .repeat-setting-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
//虚线
|
||||
margin-bottom: 10px;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&.offset {
|
||||
justify-content: center;
|
||||
}
|
||||
> .label {
|
||||
min-width: 50px;
|
||||
font-size: 14px;
|
||||
min-width: 68px;
|
||||
font-size: 12px;
|
||||
}
|
||||
> .angle-tool {
|
||||
&:not(.offset) > div {
|
||||
width: 120px;
|
||||
flex: 1;
|
||||
}
|
||||
> .slider {
|
||||
--slider-thumb-color1: #000;
|
||||
--slider-thumb-color2: #eee;
|
||||
}
|
||||
}
|
||||
> p {
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
border-bottom: 1px dashed #e5e5e5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -132,7 +132,6 @@
|
||||
v-if="v.type === 'rect'"
|
||||
trigger="click"
|
||||
destroyTooltipOnHide
|
||||
:title="t('Canvas.repeatSetting')"
|
||||
>
|
||||
<template #content>
|
||||
<repeat-setting
|
||||
@@ -164,7 +163,7 @@
|
||||
/>
|
||||
</template>
|
||||
<div class="btn">
|
||||
<i class="iconfont icon-gengduo"></i>
|
||||
<SvgIcon name="overallMore" size="18" />
|
||||
</div>
|
||||
</a-popover>
|
||||
</div>
|
||||
@@ -179,11 +178,7 @@
|
||||
import { ref, onMounted, watch, onUnmounted, reactive } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
import {
|
||||
OperationType,
|
||||
SpecialLayerId,
|
||||
SpecialType,
|
||||
} from "../../utils/layerHelper";
|
||||
import { OperationType, SpecialLayerId } from "../../utils/layerHelper";
|
||||
import { loadImageUrlToLayer } from "../../utils/imageHelper";
|
||||
import {
|
||||
calculateRotatedTopLeftDeg,
|
||||
@@ -284,9 +279,6 @@
|
||||
const getActiveObject = (e) => {
|
||||
console.log("==========切换激活对象", e, activeObjects);
|
||||
activeObjects.value = [...e.selected];
|
||||
// .filter((v) =>
|
||||
// v.specialType ? v.specialType === SpecialType.REPEAT_O : true
|
||||
// );// 过滤出印花对象
|
||||
activeObjects.value.forEach((v) => {
|
||||
v.layer = props.layerManager.getLayerById(v.layerId);
|
||||
});
|
||||
@@ -441,7 +433,7 @@
|
||||
};
|
||||
changeFill(obj, pattern);
|
||||
};
|
||||
// 改变填充便宜
|
||||
// 改变填充偏移
|
||||
const inputFillOffset = (value, obj) => {
|
||||
if (!obj.oldPattern) obj.oldPattern = obj.get("fill");
|
||||
const pattern = new fabric.Pattern({
|
||||
@@ -784,6 +776,7 @@
|
||||
}
|
||||
> .list {
|
||||
display: flex;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -29,8 +29,12 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
clothingMinIOPath: {
|
||||
type: String,
|
||||
default: "", // 衣服底图URL-线稿
|
||||
},
|
||||
});
|
||||
|
||||
console.log(props.clothingMinIOPath)
|
||||
const commandManager = inject("commandManager");
|
||||
const layerManager = inject("layerManager"); // 图层管理器
|
||||
|
||||
@@ -166,6 +170,19 @@ const normalToolsList = ref([
|
||||
icon: { name: "CFont", size: "20" },
|
||||
class: "text-btn",
|
||||
},
|
||||
{
|
||||
id: OperationType.PART,
|
||||
title: t("Canvas.GarmentPartSelector"),
|
||||
action: () => selectTool(OperationType.PART),
|
||||
icon: { name: "CPart", size: "28" },
|
||||
class: "part-btn",
|
||||
activeList: [
|
||||
OperationType.PART,
|
||||
OperationType.PART_RECTANGLE,
|
||||
OperationType.PART_BRUSH,
|
||||
OperationType.PART_ERASER,
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "help",
|
||||
title: t("Canvas.help"),
|
||||
@@ -236,7 +253,13 @@ const redGreenToolsList = ref([
|
||||
|
||||
// 根据模式选择工具列表
|
||||
const toolsList = computed(() => {
|
||||
return props.isRedGreenMode ? redGreenToolsList.value : normalToolsList.value;
|
||||
const list = props.isRedGreenMode ? redGreenToolsList.value : normalToolsList.value;
|
||||
return list.filter(tool => {
|
||||
if(tool.id === OperationType.PART){
|
||||
return !!props.clothingMinIOPath;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
function selectTool(tool, isRedGreenMode = false) {
|
||||
|
||||
@@ -1,32 +1,53 @@
|
||||
<template>
|
||||
<div class="angle-tool" :disabled="disabled">
|
||||
<div
|
||||
ref="dishRef"
|
||||
class="dish"
|
||||
@mousedown.stop="mousedown"
|
||||
@touchmove.stop="mousedown"
|
||||
>
|
||||
<div class="pointer" :style="{ transform: `rotate(${angle}deg)` }">
|
||||
<span></span>
|
||||
<template v-if="styleType === '1'">
|
||||
<div
|
||||
ref="dishRef"
|
||||
class="dish"
|
||||
@mousedown.stop="mousedown"
|
||||
@touchmove.stop="mousedown"
|
||||
>
|
||||
<div
|
||||
class="pointer"
|
||||
:style="{ transform: `rotate(${angle}deg)` }"
|
||||
>
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input
|
||||
type="number"
|
||||
v-model="angle"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input
|
||||
type="number"
|
||||
v-model="angle"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<my-input
|
||||
v-if="styleType === '2'"
|
||||
v-model="angle"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
:disabled="disabled"
|
||||
type="number"
|
||||
after="°"
|
||||
icon="icon-angle"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, watch } from "vue";
|
||||
import { calculateAngle } from "../../utils/helper";
|
||||
import MyInput from "./MyInput.vue";
|
||||
// Props
|
||||
const props = defineProps({
|
||||
styleType: {
|
||||
type: String,
|
||||
default: "1",
|
||||
},
|
||||
angle: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
@@ -139,5 +160,8 @@
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
> .my-input {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="my-input">
|
||||
<span class="decorate"></span>
|
||||
<span v-show="icon" :class="['iconfont', icon]"></span>
|
||||
<span v-show="before" class="before">{{ before }}</span>
|
||||
<input v-bind="$attrs" :value="modelValue" @input="onInput" />
|
||||
<span v-show="after" class="after">{{ after }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, watch } from "vue";
|
||||
const props = defineProps({
|
||||
modelValue: { type: Number, default: 0 },
|
||||
icon: { default: "", type: String },
|
||||
before: { default: "", type: String },
|
||||
after: { default: "", type: String },
|
||||
});
|
||||
const emit = defineEmits(["update:modelValue", "input"]);
|
||||
const onInput = (e) => {
|
||||
const value = e.target.value;
|
||||
emit("update:modelValue", value);
|
||||
emit("input", value);
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.my-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
border: 1px solid rgba(230, 230, 231, 1);
|
||||
border-radius: 3px;
|
||||
height: 20px;
|
||||
padding: 0 4px 0 2px;
|
||||
> .decorate {
|
||||
width: 2px;
|
||||
background-color: rgba(230, 230, 231, 1);
|
||||
border-radius: 3px;
|
||||
height: 85%;
|
||||
margin-right: 4px;
|
||||
}
|
||||
> .iconfont {
|
||||
font-size: 10px;
|
||||
color: #000;
|
||||
margin-right: 2px;
|
||||
}
|
||||
> .before {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
margin-right: 2px;
|
||||
}
|
||||
> .after {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
}
|
||||
> input {
|
||||
font-size: 12px;
|
||||
width: 0;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
outline: none;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,84 +1,100 @@
|
||||
<template>
|
||||
<div class="offset-tool">
|
||||
<div class="input" v-show="showInput">
|
||||
<my-input
|
||||
v-model="left"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
type="number"
|
||||
before="X"
|
||||
after="%"
|
||||
:min="-100"
|
||||
:max="100"
|
||||
/>
|
||||
<my-input
|
||||
v-model="top"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
type="number"
|
||||
before="Y"
|
||||
after="%"
|
||||
:min="-100"
|
||||
:max="100"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="dish"
|
||||
@mousedown="mousedown"
|
||||
@touchstart="mousedown"
|
||||
ref="dishRef"
|
||||
v-show="showDish"
|
||||
>
|
||||
<span
|
||||
:style="{ top: data.top + '%', left: data.left + '%' }"
|
||||
></span>
|
||||
<img src="/src/assets/images/icon/xyz.png" />
|
||||
<span class="ball" :style="ballStyle"></span>
|
||||
<span class="tip x">X: {{ left }}%</span>
|
||||
<span class="tip y">Y: {{ top }}%</span>
|
||||
<span class="line x"></span>
|
||||
<span class="line y"></span>
|
||||
<span class="line z" :style="lineZStyle"></span>
|
||||
</div>
|
||||
<input
|
||||
class="top"
|
||||
type="range"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="0.1"
|
||||
v-model="data.top"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
/>
|
||||
<input
|
||||
class="left"
|
||||
type="range"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="0.1"
|
||||
v-model="data.left"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
/>
|
||||
<span class="tip"
|
||||
>x:{{ tofix(data.left) }}% y:{{ tofix(data.top) }}%</span
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, watch } from "vue";
|
||||
import { ref, defineProps, defineEmits, watch, computed } from "vue";
|
||||
import MyInput from "./MyInput.vue";
|
||||
const props = defineProps({
|
||||
top: {
|
||||
type: Number,
|
||||
default: 50,
|
||||
},
|
||||
left: {
|
||||
type: Number,
|
||||
default: 50,
|
||||
default: 0,
|
||||
},
|
||||
top: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
showInput: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showDish: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
const tofix = (v: number | string) => Number(Number(v).toFixed(1));
|
||||
const emit = defineEmits(["change", "input"]);
|
||||
const data = reactive({
|
||||
top: tofix(props.top),
|
||||
left: tofix(props.left),
|
||||
});
|
||||
watch(
|
||||
() => props.top,
|
||||
(v) => (data.top = tofix(v))
|
||||
);
|
||||
// 工具的实际坐标 -100 ~ 100
|
||||
const top = ref(Math.round(props.top));
|
||||
const left = ref(Math.round(props.left));
|
||||
|
||||
// 原点的坐标 0 ~ 100
|
||||
const ballStyle = computed(() => ({
|
||||
top: 50 + Number(top.value) / 2 + "%",
|
||||
left: 50 + Number(left.value) / 2 + "%",
|
||||
}));
|
||||
watch(
|
||||
() => props.left,
|
||||
(v) => (data.left = tofix(v))
|
||||
(v) => (left.value = Math.round(v))
|
||||
);
|
||||
watch(
|
||||
() => props.top,
|
||||
(v) => (top.value = Math.round(v))
|
||||
);
|
||||
const dishRef = ref<HTMLDivElement>();
|
||||
const mousedown = (e: MouseEvent | TouchEvent) => {
|
||||
if (!dishRef.value) return;
|
||||
const mousemove = (e: MouseEvent | TouchEvent) => {
|
||||
if (!dishRef.value) return;
|
||||
const { left, top, width, height } =
|
||||
dishRef.value.getBoundingClientRect();
|
||||
const rect = dishRef.value.getBoundingClientRect();
|
||||
const X = e.clientX || (e as TouchEvent).touches[0].clientX;
|
||||
const Y = e.clientY || (e as TouchEvent).touches[0].clientY;
|
||||
var x = ((X - left) / width) * 100;
|
||||
var y = ((Y - top) / height) * 100;
|
||||
var x = ((X - rect.left) / rect.width) * 100;
|
||||
var y = ((Y - rect.top) / rect.height) * 100;
|
||||
if (x < 0) x = 0;
|
||||
if (x > 100) x = 100;
|
||||
if (y < 0) y = 0;
|
||||
if (y > 100) y = 100;
|
||||
data.left = tofix(x);
|
||||
data.top = tofix(y);
|
||||
left.value = Math.round((x - 50) * 2);
|
||||
top.value = Math.round((y - 50) * 2);
|
||||
onInput();
|
||||
};
|
||||
mousemove(e);
|
||||
@@ -94,96 +110,125 @@
|
||||
document.addEventListener("mouseup", mouseup);
|
||||
document.addEventListener("touchend", mouseup);
|
||||
};
|
||||
const onInput = () => emit("input", { ...data });
|
||||
const onInput = () => {
|
||||
emit("input", { left: left.value, top: top.value });
|
||||
};
|
||||
var changeTime: any = null;
|
||||
const onChange = () => {
|
||||
clearTimeout(changeTime);
|
||||
changeTime = setTimeout(() => emit("change", { ...data }), 500);
|
||||
changeTime = setTimeout(() => {
|
||||
emit("change", {
|
||||
left: left.value,
|
||||
top: top.value,
|
||||
});
|
||||
}, 500);
|
||||
};
|
||||
// var offsetTime = null;
|
||||
// watch(data, (v) => {
|
||||
// const obj = { ...v };
|
||||
// emit("input", obj);
|
||||
// clearTimeout(offsetTime);
|
||||
// offsetTime = setTimeout(() => emit("change", obj), 50);
|
||||
// });
|
||||
|
||||
// defineExpose({
|
||||
// open,
|
||||
// close,
|
||||
// });
|
||||
const lineZStyle = computed(() => ({
|
||||
"--rotateZ": calculateAngle(0, 0, left.value, top.value) + "deg",
|
||||
width: calculateDistance(0, 0, left.value, top.value) / 2 + "%",
|
||||
}));
|
||||
// 计算角度
|
||||
function calculateAngle(x1: number, y1: number, x2: number, y2: number) {
|
||||
const deltaX = x2 - x1;
|
||||
const deltaY = y1 - y2;
|
||||
let angle = Math.atan2(deltaX, deltaY) * (180 / Math.PI) - 90;
|
||||
return angle;
|
||||
}
|
||||
// 计算距离
|
||||
function calculateDistance(x1: number, y1: number, x2: number, y2: number) {
|
||||
const deltaX = x2 - x1;
|
||||
const deltaY = y2 - y1;
|
||||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
return distance;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.offset-tool {
|
||||
width: 125px;
|
||||
height: 125px;
|
||||
display: flex;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
--gap: 15px;
|
||||
> .input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
> * {
|
||||
flex: 1;
|
||||
margin-right: 12px;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .dish {
|
||||
margin: var(--gap) 0 0 var(--gap);
|
||||
flex: 1;
|
||||
border: 1px solid #000;
|
||||
border-radius: 5px;
|
||||
width: 135px;
|
||||
height: 135px;
|
||||
border: 1px solid #eaeaea;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
> span {
|
||||
background-color: #f6f6f6;
|
||||
margin-top: 24px;
|
||||
> * {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
position: absolute;
|
||||
top: 0%;
|
||||
left: 0%;
|
||||
}
|
||||
> img {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
bottom: 4px;
|
||||
right: 4px;
|
||||
}
|
||||
> .ball {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #000;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border: 1px solid #fff;
|
||||
background-color: #333;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0px 0.68px 1.7px 0px rgba(0, 0, 0, 0.26);
|
||||
}
|
||||
}
|
||||
> .tip {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
bottom: 0;
|
||||
font-size: 10px;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
color: #666;
|
||||
}
|
||||
> input.left {
|
||||
right: 0;
|
||||
}
|
||||
> input.top {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
transform-origin: left bottom;
|
||||
transform: rotate(90deg) translateX(-100%);
|
||||
}
|
||||
> input {
|
||||
position: absolute;
|
||||
width: calc(100% - var(--gap));
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: 8px;
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 0, 0, 0.1); /* 更柔和的颜色 */
|
||||
// outline: none;
|
||||
&::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #4285f4; /* 蓝色滑块 */
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
> .tip {
|
||||
font-size: 10px;
|
||||
color: #000;
|
||||
line-height: 24px;
|
||||
&.x {
|
||||
top: 50%;
|
||||
right: 0%;
|
||||
transform: translate(100%, -50%);
|
||||
padding-left: 6px;
|
||||
}
|
||||
&.y {
|
||||
top: 0%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -100%);
|
||||
}
|
||||
}
|
||||
&::-webkit-slider-thumb:hover {
|
||||
background: #3b77db;
|
||||
transform: scale(1.1);
|
||||
> .line {
|
||||
border-color: #d9d9d9;
|
||||
border-style: dashed;
|
||||
border-width: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
&.x {
|
||||
width: 100%;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
&.y {
|
||||
height: 100%;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
&.z {
|
||||
width: 50%;
|
||||
border-top-width: 1px;
|
||||
border-color: #454754;
|
||||
transform: translate(0%, -50%) rotateZ(var(--rotateZ));
|
||||
transform-origin: left center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<template>
|
||||
<div class="slider" :disabled="disabled">
|
||||
<div class="input-range">
|
||||
<span
|
||||
class="tip"
|
||||
:style="{
|
||||
'--progress': (value - props.min) / (props.max - props.min),
|
||||
}"
|
||||
>{{ props.tipFormatter(value) }}</span
|
||||
>
|
||||
<div
|
||||
class="input-range"
|
||||
:style="{
|
||||
'--progress': (value - props.min) / (props.max - props.min),
|
||||
}"
|
||||
>
|
||||
<span class="tip">{{ props.tipFormatter(value) }}</span>
|
||||
<input
|
||||
type="range"
|
||||
v-model="value"
|
||||
@@ -20,8 +19,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="input" v-show="isInput">
|
||||
<input
|
||||
type="number"
|
||||
<my-input
|
||||
v-model="value"
|
||||
:min="props.min"
|
||||
:max="props.max"
|
||||
@@ -29,6 +27,7 @@
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
:disabled="disabled"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,6 +35,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, watch } from "vue";
|
||||
import MyInput from "./MyInput.vue";
|
||||
const props = defineProps({
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
@@ -86,9 +86,10 @@
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
--input-thumb-size: 12px;
|
||||
width: 150px;
|
||||
// &:focus-within,
|
||||
--input-thumb-size: 10px;
|
||||
--backcolor1: var(--slider-thumb-color1, #4285f4);
|
||||
--backcolor2: var(--slider-thumb-color2, rgba(0, 0, 0, 0.1));
|
||||
&:hover {
|
||||
> .input-range > .tip {
|
||||
display: block;
|
||||
@@ -103,21 +104,26 @@
|
||||
appearance: none;
|
||||
height: 5px;
|
||||
border-radius: 5px;
|
||||
background: rgba(0, 0, 0, 0.1); /* 更柔和的颜色 */
|
||||
outline: none;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
var(--backcolor1) 0%,
|
||||
var(--backcolor1) calc(var(--progress) * 100%),
|
||||
var(--backcolor2) calc(var(--progress) * 100%),
|
||||
var(--backcolor2) 100%
|
||||
);
|
||||
&::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: var(--input-thumb-size);
|
||||
height: var(--input-thumb-size);
|
||||
border-radius: 50%;
|
||||
background: #4285f4; /* 蓝色滑块 */
|
||||
background: var(--backcolor1); /* 蓝色滑块 */
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
&::-webkit-slider-thumb:hover {
|
||||
background: #3b77db;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { KeyboardManager } from "./managers/events/KeyboardManager.js";
|
||||
import CanvasConfig from "./config/canvasConfig.js";
|
||||
import { LiquifyManager } from "./managers/liquify/LiquifyManager";
|
||||
import { SelectionManager } from "./managers/selection/SelectionManager";
|
||||
import { PartManager } from "./managers/PartManager";
|
||||
import { RedGreenModeManager } from "./managers/RedGreenModeManager";
|
||||
import texturePresetManager from "./managers/brushes/TexturePresetManager";
|
||||
import { BrushStore } from "./store/BrushStore";
|
||||
@@ -38,6 +39,7 @@ import LiquifyPanel from "./components/LiquifyPanel.vue"; // 引入液化编辑
|
||||
import PalletPanel from "./components/PalletPanel/index.vue";
|
||||
import SelectMenuPanel from "./components/SelectMenuPanel/index.vue"; // 引入选择工具菜单组件
|
||||
import SelectionPanel from "./components/SelectionPanel.vue"; // 引入选区面板
|
||||
import PartSelectorPanel from "./components/PartSelectorPanel.vue"; // 引入部件选取面板
|
||||
import { LayerType, OperationType } from "./utils/layerHelper.js";
|
||||
import { ToolManager } from "./managers/ToolManager.js";
|
||||
import { fabric } from "fabric-with-all";
|
||||
@@ -45,6 +47,7 @@ import {
|
||||
uploadImageAndCreateLayer,
|
||||
loadImageUrlToLayer,
|
||||
loadImage,
|
||||
resizeImage,
|
||||
} from "./utils/imageHelper.js";
|
||||
import { optimizeCanvasRendering } from "./utils/helper";
|
||||
// import MinimapPanel from "./components/MinimapPanel.vue";
|
||||
@@ -84,6 +87,10 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false, // 是否启用红绿图模式
|
||||
},
|
||||
clothingMinIOPath: {
|
||||
type: String,
|
||||
default: "", // 衣服底图URL-线稿
|
||||
},
|
||||
clothingImageUrl: {
|
||||
type: String,
|
||||
default: "", // 衣服底图URL-线稿
|
||||
@@ -184,6 +191,7 @@ let keyboardManager = null;
|
||||
let toolManager = null;
|
||||
let liquifyManager = null;
|
||||
let selectionManager = null;
|
||||
let partManager = null;
|
||||
let redGreenModeManager = null;
|
||||
|
||||
// 快捷键帮助模态框状态
|
||||
@@ -225,6 +233,7 @@ function handleCanvasInit(isLoadJson = false) {
|
||||
keyboardManager,
|
||||
liquifyManager,
|
||||
selectionManager,
|
||||
partManager,
|
||||
redGreenModeManager,
|
||||
});
|
||||
}
|
||||
@@ -374,9 +383,20 @@ onMounted(async () => {
|
||||
selectionManager = new SelectionManager({
|
||||
canvas: canvasManager.canvas,
|
||||
layerManager,
|
||||
props,
|
||||
});
|
||||
canvasManager.setSelectionManager(selectionManager);
|
||||
|
||||
// 初始化部件选择管理器
|
||||
partManager = new PartManager({
|
||||
canvas: canvasManager.canvas,
|
||||
layerManager,
|
||||
canvasManager,
|
||||
toolManager,
|
||||
props,
|
||||
});
|
||||
canvasManager.setPartManager(partManager);
|
||||
|
||||
if (props.canvasJSON) {
|
||||
// 如果传入了初始JSON数据,加载到画布上
|
||||
if (typeof props.canvasJSON === "string") {
|
||||
@@ -536,6 +556,7 @@ onBeforeUnmount(async () => {
|
||||
toolManager?.dispose?.();
|
||||
liquifyManager?.dispose?.();
|
||||
selectionManager?.dispose?.();
|
||||
partManager?.dispose?.();
|
||||
redGreenModeManager?.dispose?.();
|
||||
// minimapManager?.dispose?.();
|
||||
canvasManager = null;
|
||||
@@ -545,6 +566,7 @@ onBeforeUnmount(async () => {
|
||||
toolManager = null;
|
||||
liquifyManager = null;
|
||||
selectionManager = null;
|
||||
partManager = null;
|
||||
redGreenModeManager = null;
|
||||
// fabric.Object.prototype.controls.deleteControl = undefined;
|
||||
|
||||
@@ -709,8 +731,13 @@ function addRemoveBtn(fun) {
|
||||
});
|
||||
}
|
||||
|
||||
function deleteFun() {
|
||||
removeLayer(layerManager.activeLayerId.value);
|
||||
function deleteFun(e, control) {
|
||||
const target = control.target;
|
||||
if(target.onDelete){
|
||||
target.onDelete(target);
|
||||
}else if(target.id){
|
||||
removeLayer(layerManager.activeLayerId.value);
|
||||
}
|
||||
}
|
||||
|
||||
function removeLayer(layerId) {
|
||||
@@ -995,26 +1022,38 @@ defineExpose({
|
||||
);
|
||||
},
|
||||
// 导出图片
|
||||
exportImage: ({
|
||||
exportImage: async ({
|
||||
isContainBg = false, // 是否包含背景图层
|
||||
isContainFixed = false, // 是否包含固定图层
|
||||
isContainFixedOther = false, // 是否包含其他固定图层
|
||||
isContainFixedOther = true, // 是否包含其他固定图层--颜色图层
|
||||
isPrintTrimsNoRepeat = true, // 是否包含印花图层的不平铺
|
||||
isPrintTrimsRepeat = true, // 是否包含印花图层的平铺
|
||||
isContainNormalLayer = true, // 是否包含普通图层
|
||||
isCropByBg = false, // 是否使用背景大小裁剪 // 如果为true,则导出时裁剪到背景图层大小
|
||||
layerId = "", // 导出具体图层ID
|
||||
layerIdArray = [], // 导出多个图层ID数组
|
||||
expPicType = "png", // 导出图片类型 JPG 或 PNG ,SVG
|
||||
isEnhanceImg, // 是否是增强图片
|
||||
width = 0,// 导出的图片宽度
|
||||
height = 0, // 导出的图片高度
|
||||
} = {}) => {
|
||||
return canvasManager.exportImage({
|
||||
var base64 = await canvasManager.exportImage({
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther,
|
||||
isPrintTrimsNoRepeat,
|
||||
isPrintTrimsRepeat,
|
||||
isContainNormalLayer,
|
||||
isCropByBg,
|
||||
layerId,
|
||||
layerIdArray,
|
||||
expPicType,
|
||||
isEnhanceImg,
|
||||
});
|
||||
if(width > 0 && height > 0){
|
||||
base64 = await resizeImage(base64, width, height);
|
||||
}
|
||||
return base64;
|
||||
},
|
||||
// 导出颜色图层
|
||||
exportColorLayer: () => {
|
||||
@@ -1177,6 +1216,7 @@ defineExpose({
|
||||
v-if="canvasManagerLoaded"
|
||||
:activeTool="activeTool"
|
||||
:isRedGreenMode="isRedGreenMode"
|
||||
:clothingMinIOPath="props.clothingMinIOPath"
|
||||
@tool-selected="handleToolSelect"
|
||||
@red-green-tool-selected="handleRedGreenToolSelect"
|
||||
@toggle-red-green-mode="toggleRedGreenMode"
|
||||
@@ -1248,6 +1288,19 @@ defineExpose({
|
||||
:activeTool="activeTool"
|
||||
/>
|
||||
|
||||
<!-- 部件选取面板 -->
|
||||
<PartSelectorPanel
|
||||
v-if="canvasManagerLoaded && !enabledRedGreenMode"
|
||||
:canvas="canvasManager && canvasManager.canvas"
|
||||
:commandManager="commandManager"
|
||||
:selectionManager="selectionManager"
|
||||
:partManager="partManager"
|
||||
:layerManager="layerManager"
|
||||
:canvasManager="canvasManager"
|
||||
:toolManager="toolManager"
|
||||
:activeTool="activeTool"
|
||||
/>
|
||||
|
||||
<!-- 文本编辑面板 -->
|
||||
<TextEditorPanel
|
||||
v-if="canvasManagerLoaded && !enabledRedGreenMode"
|
||||
@@ -1401,6 +1454,7 @@ defineExpose({
|
||||
/* background-color: #f8f8f8; */
|
||||
:deep(.canvas-container) {
|
||||
position: absolute !important;
|
||||
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1413,33 +1467,31 @@ defineExpose({
|
||||
}
|
||||
|
||||
.background-grid {
|
||||
--offsetX: 0px;
|
||||
--offsetY: 0px;
|
||||
--size: 8px;
|
||||
--color: #dedcdc;
|
||||
--offsetX: 50%;
|
||||
--offsetY: 50%;
|
||||
--size: 10px;
|
||||
--color: rgba(229, 229,229,0.5);
|
||||
background-image: -webkit-linear-gradient(
|
||||
45deg,
|
||||
var(--color) 25%,
|
||||
90deg,
|
||||
var(--color) 1px,
|
||||
transparent 0,
|
||||
transparent 75%,
|
||||
var(--color) 0
|
||||
),
|
||||
-webkit-linear-gradient(45deg, var(--color) 25%, transparent 0, transparent
|
||||
75%, var(--color) 0);
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
var(--color) 25%,
|
||||
-webkit-linear-gradient(
|
||||
0,
|
||||
var(--color) 1px,
|
||||
transparent 0,
|
||||
);
|
||||
background-image:linear-gradient(
|
||||
90deg,
|
||||
var(--color) 1px,
|
||||
transparent 0,
|
||||
transparent 75%,
|
||||
var(--color) 0
|
||||
),
|
||||
linear-gradient(
|
||||
45deg,
|
||||
var(--color) 25%,
|
||||
0,
|
||||
var(--color) 1px,
|
||||
transparent 0,
|
||||
transparent 75%,
|
||||
var(--color) 0
|
||||
);
|
||||
background-color: #fafafa;
|
||||
background-position: var(--offsetX) var(--offsetY),
|
||||
calc(var(--size) + var(--offsetX)) calc(var(--size) + var(--offsetY));
|
||||
background-size: calc(var(--size) * 2) calc(var(--size) * 2);
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
createLayer,
|
||||
LayerType,
|
||||
SpecialLayerId,
|
||||
SpecialType,
|
||||
BlendMode,
|
||||
} from "../utils/layerHelper";
|
||||
import { ObjectMoveCommand } from "../commands/ObjectCommands";
|
||||
@@ -35,6 +34,7 @@ import {
|
||||
createPatternTransform,
|
||||
getTransformScaleAngle,
|
||||
base64ToCanvas,
|
||||
imageAddGapToCanvas,
|
||||
} from "../utils/helper";
|
||||
import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands";
|
||||
import { isFunction } from "lodash-es";
|
||||
@@ -45,7 +45,7 @@ import {
|
||||
} from "../utils/layerUtils";
|
||||
import { imageModeHandler } from "../utils/imageHelper";
|
||||
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 i18n from "@/lang/index.ts";
|
||||
const {t} = i18n.global;
|
||||
@@ -305,6 +305,19 @@ export class CanvasManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置部件选择管理器
|
||||
* @param {Object} partManager 部件选择管理器实例
|
||||
*/
|
||||
setPartManager(partManager) {
|
||||
this.partManager = partManager;
|
||||
|
||||
// 如果已创建事件管理器,更新它的部件选择管理器引用
|
||||
if (this.eventManager) {
|
||||
this.eventManager.partManager = this.partManager;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置红绿图模式管理器
|
||||
setRedGreenModeManager(redGreenModeManager) {
|
||||
this.redGreenModeManager = redGreenModeManager;
|
||||
@@ -717,6 +730,8 @@ export class CanvasManager {
|
||||
originX: backgroundLayerObject.originX || "left",
|
||||
originY: backgroundLayerObject.originY || "top",
|
||||
absolutePositioned: true,
|
||||
rx: 15,
|
||||
ry: 15,
|
||||
});
|
||||
}
|
||||
getBackgroundLayer() {
|
||||
@@ -827,7 +842,7 @@ export class CanvasManager {
|
||||
updateMaskPosition(backgroundLayerObject) {
|
||||
if (!backgroundLayerObject || !this.maskLayer || !this.canvas.clipPath)
|
||||
return;
|
||||
|
||||
console.log("backgroundLayerObject");
|
||||
const left = backgroundLayerObject.left;
|
||||
const top = backgroundLayerObject.top;
|
||||
|
||||
@@ -922,6 +937,9 @@ export class CanvasManager {
|
||||
* @param {Boolean} options.isContainBg 是否包含背景图层
|
||||
* @param {Boolean} options.isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} options.isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} options.isPrintTrimsNoRepeat 是否包含印花图层的不平铺
|
||||
* @param {Boolean} options.isPrintTrimsRepeat 是否包含印花图层的平铺
|
||||
* @param {Boolean} options.isContainNormalLayer 是否包含普通图层
|
||||
* @param {String} options.layerId 导出具体图层ID
|
||||
* @param {Array} options.layerIdArray 导出多个图层ID数组
|
||||
* @param {String} options.expPicType 导出图片类型 (png/jpg/svg)
|
||||
@@ -942,6 +960,9 @@ export class CanvasManager {
|
||||
// this.canvas.renderAll(); // 重新渲染画布
|
||||
// 自动设置红绿图模式相关参数
|
||||
const enhancedOptions = {
|
||||
isPrintTrimsNoRepeat: true,
|
||||
isPrintTrimsRepeat: true,
|
||||
isContainNormalLayer: true,
|
||||
...options,
|
||||
// 如果没有明确指定,则根据当前模式自动设置
|
||||
restoreOpacityInRedGreen:
|
||||
@@ -972,7 +993,46 @@ export class CanvasManager {
|
||||
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) {
|
||||
console.warn("CanvasManager导出图片失败:", error);
|
||||
throw error;
|
||||
@@ -989,10 +1049,12 @@ export class CanvasManager {
|
||||
// 导出印花和元素图层信息
|
||||
const printTrimsData = await this.exportPrintTrimsLayers().catch(() => ({prints: null, trims: null}));
|
||||
|
||||
return {
|
||||
const obj = {
|
||||
color,
|
||||
...printTrimsData,
|
||||
};
|
||||
console.log("==========exportExtraInfo:", obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1533,7 +1595,7 @@ export class CanvasManager {
|
||||
selectable: true,
|
||||
hasControls: true,
|
||||
hasBorders: true,
|
||||
specialType: SpecialType.PRINT_TRIMS_O,
|
||||
isPrintTrims: true,
|
||||
globalCompositeOperation: BlendMode.MULTIPLY,
|
||||
});
|
||||
resolve(fabricImage);
|
||||
@@ -1547,7 +1609,7 @@ export class CanvasManager {
|
||||
visible: true,
|
||||
locked: false,
|
||||
opacity: 1.0,
|
||||
specialType: SpecialType.PRINT_TRIMS_L,
|
||||
isPrintTrims: true,
|
||||
blendMode: BlendMode.MULTIPLY,
|
||||
fabricObjects: [image.toObject(["id", "layerId", "layerName"])],
|
||||
metadata: {sourceData: item},
|
||||
@@ -1571,37 +1633,57 @@ export class CanvasManager {
|
||||
resolve(tcanvas);
|
||||
}, { crossOrigin: "anonymous" });
|
||||
})
|
||||
let scaleX = fixedLayerObj.width / image.width * (item.scale?.[0] || 1) / 5;
|
||||
let scaleY = fixedLayerObj.height / image.height * (item.scale?.[1] || 1) / 5;
|
||||
let scale = fixedLayerObj.width > fixedLayerObj.height ? scaleX : scaleY;
|
||||
let left = (item.location?.[0] || 0) - image.width * scale / 2
|
||||
let top = (item.location?.[1] || 0) - image.height * scale / 2
|
||||
let scaleX_ = fixedLayerObj.width / image.width * (item.scale?.[0] || 1) / 5;
|
||||
let scaleY_ = fixedLayerObj.height / image.height * (item.scale?.[1] || 1) / 5;
|
||||
let scale = fixedLayerObj.width > fixedLayerObj.height ? scaleX_ : scaleY_;
|
||||
let offsetX = (item.location?.[0] || 0) - image.width * scale / 2
|
||||
let offsetY = (item.location?.[1] || 0) - image.height * scale / 2
|
||||
let top = fixedLayerObj.top - fixedLayerObj.height * fixedLayerObj.scaleY / 2
|
||||
let left = fixedLayerObj.left - fixedLayerObj.width * fixedLayerObj.scaleX / 2
|
||||
let scaleX = fixedLayerObj.scaleX
|
||||
let scaleY = fixedLayerObj.scaleY
|
||||
let opacity = 1
|
||||
let angle = 0
|
||||
let gapX = 0
|
||||
let gapY = 0
|
||||
let fillSource = image
|
||||
if(item.object){
|
||||
top += item.object.top * fixedLayerObj.scaleY
|
||||
left += item.object.left * fixedLayerObj.scaleX
|
||||
scaleX *= item.object.scaleX
|
||||
scaleY *= item.object.scaleY
|
||||
opacity = item.object.opacity
|
||||
angle = item.object.angle
|
||||
gapX = item.object.gapX
|
||||
gapY = item.object.gapY
|
||||
fillSource = imageAddGapToCanvas(image, gapX, gapY);
|
||||
}
|
||||
let rect = new fabric.Rect({
|
||||
id: id,
|
||||
layerId: id,
|
||||
layerName: name,
|
||||
width: fixedLayerObj.width,
|
||||
height: fixedLayerObj.height,
|
||||
top: fixedLayerObj.top - fixedLayerObj.height * fixedLayerObj.scaleY / 2,
|
||||
left: fixedLayerObj.left - fixedLayerObj.width * fixedLayerObj.scaleX / 2,
|
||||
scaleX: fixedLayerObj.scaleX,
|
||||
scaleY: fixedLayerObj.scaleY,
|
||||
top: top,
|
||||
left: left,
|
||||
scaleX: scaleX,
|
||||
scaleY: scaleY,
|
||||
globalCompositeOperation: BlendMode.MULTIPLY,
|
||||
fill: new fabric.Pattern({
|
||||
source: image,
|
||||
source: fillSource,
|
||||
repeat: "repeat",
|
||||
patternTransform: createPatternTransform(scale, item.angle || 0),
|
||||
offsetX: left, // 水平偏移
|
||||
offsetY: top, // 垂直偏移
|
||||
offsetX: offsetX, // 水平偏移
|
||||
offsetY: offsetY, // 垂直偏移
|
||||
}),
|
||||
fill_ : {
|
||||
source: item.path,
|
||||
gapX: 0,
|
||||
gapY: 0,
|
||||
gapX: gapX,
|
||||
gapY: gapY,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
},
|
||||
specialType: SpecialType.REPEAT_O,
|
||||
isPrintTrims: true,
|
||||
});
|
||||
this.canvas.add(rect);
|
||||
let layer = createLayer({
|
||||
@@ -1611,7 +1693,7 @@ export class CanvasManager {
|
||||
visible: true,
|
||||
locked: true,
|
||||
opacity: 1,
|
||||
specialType: SpecialType.REPEAT_L,
|
||||
isPrintTrims: true,
|
||||
blendMode: BlendMode.MULTIPLY,
|
||||
fabricObjects: [rect.toObject(["id", "layerId", "layerName"])],
|
||||
metadata: {sourceData: item},
|
||||
@@ -1630,7 +1712,7 @@ export class CanvasManager {
|
||||
// })
|
||||
// children.push(layer);
|
||||
// }
|
||||
// if(children.length === 0) return;
|
||||
if(children.length === 0) return;
|
||||
const groupRect = new fabric.Rect({});
|
||||
await this.setObjecCliptInfo(groupRect);
|
||||
// 插入组图层
|
||||
@@ -1646,11 +1728,8 @@ export class CanvasManager {
|
||||
children: children,
|
||||
clippingMask: groupRect.toObject(),
|
||||
isPrintTrimsGroup: true,
|
||||
specialType: SpecialType.PRINT_TRIMS_G,
|
||||
});
|
||||
this.layers.value.splice(groupIndex, 0, groupLayer);
|
||||
console.log("==========layers", [...this.layers.value]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,7 @@ export class ExportManager {
|
||||
* @param {Boolean} options.isContainBg 是否包含背景图层
|
||||
* @param {Boolean} options.isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} options.isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} options.isContainNormalLayer 是否包含普通图层
|
||||
* @param {Boolean} options.isCropByBg 是否使用背景大小裁剪
|
||||
* @param {String} options.layerId 导出具体图层ID
|
||||
* @param {Array} options.layerIdArray 导出多个图层ID数组
|
||||
@@ -35,6 +36,7 @@ export class ExportManager {
|
||||
isContainBg = false,
|
||||
isContainFixed = false,
|
||||
isContainFixedOther = false, // 是否包含其他固定图层
|
||||
isContainNormalLayer = true, // 是否包含普通图层
|
||||
isCropByBg = false, // 是否使用背景大小裁剪
|
||||
layerId = "",
|
||||
layerIdArray = [],
|
||||
@@ -68,6 +70,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer, // 是否包含普通图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg,
|
||||
@@ -81,6 +84,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer, // 是否包含普通图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg,
|
||||
@@ -160,6 +164,7 @@ export class ExportManager {
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||
* @param {Boolean} isRedGreenMode 是否为红绿图模式
|
||||
* @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
|
||||
* @param {Boolean} isCropByBg 是否使用背景大小裁剪
|
||||
@@ -173,6 +178,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer = true, // 是否包含普通图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg, // 是否使用背景大小裁剪
|
||||
@@ -188,6 +194,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer, // 是否包含普通图层
|
||||
);
|
||||
|
||||
if (objectsToExport.length === 0) {
|
||||
@@ -220,6 +227,7 @@ export class ExportManager {
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||
* @param {Boolean} isRedGreenMode 是否为红绿图模式
|
||||
* @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
|
||||
* @param {Boolean} isCropByBg 是否使用背景大小裁剪
|
||||
@@ -233,6 +241,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer, // 是否包含普通图层
|
||||
isRedGreenMode,
|
||||
restoreOpacityInRedGreen,
|
||||
isCropByBg, // 是否使用背景大小裁剪
|
||||
@@ -246,6 +255,7 @@ export class ExportManager {
|
||||
isContainBg,
|
||||
isContainFixed,
|
||||
isContainFixedOther, // 是否包含其他固定图层
|
||||
isContainNormalLayer, // 是否包含普通图层
|
||||
excludedLayers,
|
||||
);
|
||||
|
||||
@@ -405,11 +415,12 @@ export class ExportManager {
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||
* @param {Array} excludedLayers 排除的图层ID数组
|
||||
* @returns {Array} 按正确顺序排列的真实对象数组
|
||||
* @private
|
||||
*/
|
||||
_collectObjectsByLayerOrder(layerIdArray, isContainBg, isContainFixed, isContainFixedOther, excludedLayers) {
|
||||
_collectObjectsByLayerOrder(layerIdArray, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer, excludedLayers) {
|
||||
const objectsToExport = [];
|
||||
const allLayers = this._getAllLayersFlattened(excludedLayers); // 获取扁平化的图层列表
|
||||
|
||||
@@ -421,7 +432,7 @@ export class ExportManager {
|
||||
if (layerIdArray && !layerIdArray.includes(layer.id)) continue;
|
||||
|
||||
// 检查图层类型过滤条件
|
||||
if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther))
|
||||
if (!this._shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer))
|
||||
continue;
|
||||
|
||||
if (layer.visible) {
|
||||
@@ -1040,10 +1051,11 @@ export class ExportManager {
|
||||
* @param {Boolean} isContainBg 是否包含背景图层
|
||||
* @param {Boolean} isContainFixed 是否包含固定图层
|
||||
* @param {Boolean} isContainFixedOther 是否包含其他固定图层
|
||||
* @param {Boolean} isContainNormalLayer 是否包含普通图层
|
||||
* @returns {Boolean} 是否应该包含
|
||||
* @private
|
||||
*/
|
||||
_shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther) {
|
||||
_shouldIncludeLayer(layer, isContainBg, isContainFixed, isContainFixedOther, isContainNormalLayer) {
|
||||
if (!layer) return false;
|
||||
|
||||
// 检查背景图层
|
||||
@@ -1061,7 +1073,12 @@ export class ExportManager {
|
||||
return isContainFixedOther;
|
||||
}
|
||||
|
||||
// 普通图层总是包含
|
||||
return true;
|
||||
// 印花图层始终导出
|
||||
if (layer.isPrintTrims || layer.isPrintTrimsGroup) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 普通图层
|
||||
return isContainNormalLayer;
|
||||
}
|
||||
}
|
||||
|
||||
325
src/component/Canvas/CanvasEditor/managers/PartManager.js
Normal file
@@ -0,0 +1,325 @@
|
||||
import { fabric } from "fabric-with-all";
|
||||
import { generateId } from "../utils/helper";
|
||||
import { OperationType } from "../utils/layerHelper";
|
||||
import { CreateSelectionCommand } from "../commands/SelectionCommands";
|
||||
import { ClearSelectionCommand } from "../commands/LassoCutoutCommand";
|
||||
import addIcon from "@/assets/images/canvas/add.png";
|
||||
import removeIcon from "@/assets/images/canvas/remove.png";
|
||||
import { Https } from "@/tool/https";
|
||||
import store from "@/store";
|
||||
|
||||
/**
|
||||
* 部件选择管理器
|
||||
*/
|
||||
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.layerManager = options.layerManager;
|
||||
this.canvasManager = options.canvasManager;
|
||||
this.toolManager = options.toolManager;
|
||||
this.props = options.props;
|
||||
|
||||
// 状态
|
||||
this.isActive = false;
|
||||
this.partObject = null; // 当前选区对象
|
||||
this.partId = "part_selector";
|
||||
this.defaultCursor = "default";
|
||||
|
||||
// 绘制状态
|
||||
this.drawingObject = null;
|
||||
this.startPoint = null;
|
||||
this.partPath = null; // 存储选区路径数据
|
||||
|
||||
|
||||
// 不再直接绑定事件处理函数
|
||||
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.onSelectionChanged = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前工具
|
||||
* @param {String} toolId 工具ID
|
||||
*/
|
||||
setCurrentTool(toolId) {
|
||||
// 检查是否为选区工具
|
||||
const wasActive = this.isActive;
|
||||
this.isActive = this.tools.includes(toolId);
|
||||
|
||||
// 如果从非选区工具切换到选区工具,初始化事件
|
||||
if (!wasActive && this.isActive) {
|
||||
this.initEvents();
|
||||
}
|
||||
// 如果从选区工具切换到非选区工具,清理事件和选区
|
||||
else if (wasActive && !this.isActive) {
|
||||
this.cleanupEvents();
|
||||
this.clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化选区相关事件
|
||||
*/
|
||||
initEvents() {
|
||||
if (!this.canvas || this._mouseDownHandler) return; // 避免重复初始化
|
||||
this.defaultCursor = this.canvas.defaultCursor;
|
||||
|
||||
// 保存实例引用,用于事件处理函数中
|
||||
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) {
|
||||
const button = options.button;
|
||||
const isLeft = button === 1;// 左键1(添加) 右键3(删除)
|
||||
const icon = `url("${isLeft ? addIcon : removeIcon}") 16 16, default`
|
||||
this.canvas.upperCanvasEl.style.cursor = icon;
|
||||
}
|
||||
// 点选工具模式下移动事件处理
|
||||
_pointMoveHandler(options) { }
|
||||
// 点选工具模式下抬起事件处理
|
||||
_pointUpHandler(options) {
|
||||
const button = options.button;
|
||||
const isLeft = button === 1;// 左键1(添加) 右键3(删除)
|
||||
this.canvas.upperCanvasEl.style.cursor = this.defaultCursor;
|
||||
const fixedObject = this.canvasManager.getFixedLayerObject();
|
||||
if (!fixedObject) return console.warn("未找到固定图层");
|
||||
const { x, y } = options.pointer;
|
||||
const width = fixedObject.width * fixedObject.scaleX;
|
||||
const height = fixedObject.height * fixedObject.scaleY;
|
||||
const X = x - (fixedObject.left - width / 2);
|
||||
const Y = y - (fixedObject.top - height / 2);
|
||||
this.getSegAnythingImage({
|
||||
image_path: "aida-users/24299/sketch/70bb39cc-63e0-44a9-a627-3542d0f9cd70.png",
|
||||
type: "point",
|
||||
points: [[X, Y], [X - 10, Y - 10]],
|
||||
labels: [1, 0],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 框选工具模式下点击事件处理
|
||||
_rectangleDownHandler(options) {
|
||||
}
|
||||
// 框选工具模式下移动事件处理
|
||||
_rectangleMoveHandler(options) {
|
||||
|
||||
}
|
||||
// 框选工具模式下抬起事件处理
|
||||
_rectangleUpHandler(options) {
|
||||
}
|
||||
|
||||
|
||||
// 绘制工具模式下点击事件处理
|
||||
_brushDownHandler(options) {
|
||||
}
|
||||
// 绘制工具模式下移动事件处理
|
||||
_brushMoveHandler(options) {
|
||||
|
||||
}
|
||||
// 绘制工具模式下抬起事件处理
|
||||
_brushUpHandler(options) {
|
||||
}
|
||||
|
||||
|
||||
// 擦除工具模式下抬起事件处理
|
||||
_eraseUpHandler(options) {
|
||||
}
|
||||
// 擦除工具模式下点击事件处理
|
||||
_eraseDownHandler(options) {
|
||||
}
|
||||
// 擦除工具模式下移动事件处理
|
||||
_eraseMoveHandler(options) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 获取分隔后图片
|
||||
async getSegAnythingImage(obj) {
|
||||
const user_id = store.state.UserHabit.userDetail.userId;
|
||||
const data = {
|
||||
user_id,
|
||||
...obj,
|
||||
}
|
||||
Https.axiosPost(Https.httpUrls.segAnything, data)
|
||||
.then(response => {
|
||||
console.log(response);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除选区
|
||||
*/
|
||||
clearSelection() {
|
||||
// 移除选区对象
|
||||
// this.removeSelectionFromCanvas();
|
||||
|
||||
// 重置选区状态
|
||||
this.partObject = null;
|
||||
this.partPath = null;
|
||||
|
||||
// 触发选区变化回调
|
||||
if (this.onSelectionChanged && typeof this.onSelectionChanged === "function") {
|
||||
this.onSelectionChanged();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理资源
|
||||
*/
|
||||
dispose() {
|
||||
this.cleanupEvents();
|
||||
this.clearSelection();
|
||||
this.canvas = null;
|
||||
this.commandManager = null;
|
||||
this.layerManager = null;
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ export class ToolManager {
|
||||
this.activeTool = options.activeTool || {
|
||||
value: OperationType.SELECT,
|
||||
};
|
||||
this.props = options.props;
|
||||
|
||||
// 红绿图模式状态
|
||||
this.isRedGreenMode = false;
|
||||
@@ -67,6 +68,12 @@ export class ToolManager {
|
||||
|
||||
// 工具列表 - 与OperationType保持一致
|
||||
this.tools = {
|
||||
// 禁用工具
|
||||
[OperationType.DISABLED]: {
|
||||
name: "禁用工具",
|
||||
icon: "disabled",
|
||||
cursor: "not-allowed",
|
||||
},
|
||||
// 基础工具
|
||||
[OperationType.SELECT]: {
|
||||
name: "选择工具",
|
||||
@@ -83,6 +90,7 @@ export class ToolManager {
|
||||
shortcut: "B",
|
||||
setup: this.setupBrushTool.bind(this),
|
||||
allowedInRedGreen: false,
|
||||
specialLayerDisabled: true,
|
||||
},
|
||||
[OperationType.ERASER]: {
|
||||
name: "橡皮擦",
|
||||
@@ -91,6 +99,7 @@ export class ToolManager {
|
||||
shortcut: "E",
|
||||
setup: this.setupEraserTool.bind(this),
|
||||
allowedInRedGreen: true, // 红绿图模式允许橡皮擦
|
||||
specialLayerDisabled: true,
|
||||
},
|
||||
[OperationType.EYEDROPPER]: {
|
||||
name: "吸色工具",
|
||||
@@ -117,6 +126,7 @@ export class ToolManager {
|
||||
shortcut: "L",
|
||||
setup: this.setupLassoTool.bind(this),
|
||||
allowedInRedGreen: false,
|
||||
specialLayerDisabled: true,
|
||||
},
|
||||
[OperationType.LASSO_RECTANGLE]: {
|
||||
name: "矩形套索工具",
|
||||
@@ -126,6 +136,7 @@ export class ToolManager {
|
||||
altKey: true,
|
||||
setup: this.setupRectangleLassoTool.bind(this),
|
||||
allowedInRedGreen: false,
|
||||
specialLayerDisabled: true,
|
||||
},
|
||||
[OperationType.LASSO_ELLIPSE]: {
|
||||
name: "椭圆形套索工具",
|
||||
@@ -135,6 +146,7 @@ export class ToolManager {
|
||||
altKey: true,
|
||||
setup: this.setupEllipseLassoTool.bind(this),
|
||||
allowedInRedGreen: false,
|
||||
specialLayerDisabled: true,
|
||||
},
|
||||
|
||||
// 选区工具 - 只需要矩形选区
|
||||
@@ -164,6 +176,7 @@ export class ToolManager {
|
||||
shortcut: "J",
|
||||
setup: this.setupLiquifyTool.bind(this),
|
||||
allowedInRedGreen: false, // 红绿图模式不允许液化
|
||||
specialLayerDisabled: true,
|
||||
},
|
||||
[OperationType.TEXT]: {
|
||||
name: "文本工具",
|
||||
@@ -174,6 +187,32 @@ export class ToolManager {
|
||||
allowedInRedGreen: false, // 红绿图模式不允许文本
|
||||
},
|
||||
|
||||
// 部件选取工具
|
||||
[OperationType.PART]: {
|
||||
name: "部件选取工具",
|
||||
icon: "part",
|
||||
cursor: "default",
|
||||
setup: this.setupPartTool.bind(this),
|
||||
},
|
||||
[OperationType.PART_RECTANGLE]: {
|
||||
name: "部件选取工具-矩形",
|
||||
icon: "part",
|
||||
cursor: "default",
|
||||
setup: this.setupPartRectangleTool.bind(this),
|
||||
},
|
||||
[OperationType.PART_BRUSH]: {
|
||||
name: "部件选取工具-画笔",
|
||||
icon: "part",
|
||||
cursor: "default",
|
||||
setup: this.setupPartBrushTool.bind(this),
|
||||
},
|
||||
[OperationType.PART_ERASER]: {
|
||||
name: "部件选取工具-橡皮擦",
|
||||
icon: "part",
|
||||
cursor: "default",
|
||||
setup: this.setupPartEraserTool.bind(this),
|
||||
},
|
||||
|
||||
// 红绿图模式专用工具
|
||||
[OperationType.RED_BRUSH]: {
|
||||
name: "红色笔刷",
|
||||
@@ -331,8 +370,9 @@ export class ToolManager {
|
||||
* @param {String} toolId 工具ID
|
||||
*/
|
||||
setTool(toolId) {
|
||||
const tool = this.tools[toolId];
|
||||
// 检查工具是否存在
|
||||
if (!this.tools[toolId]) {
|
||||
if (!tool) {
|
||||
console.error(`工具 '${toolId}' 不存在`);
|
||||
return;
|
||||
}
|
||||
@@ -348,15 +388,20 @@ export class ToolManager {
|
||||
console.warn(`工具 '${toolId}' 只能在红绿图模式下使用`);
|
||||
return;
|
||||
}
|
||||
if(tool?.specialLayerDisabled && this.checkToolCanOperateSelectedObject()){
|
||||
console.warn(`工具 '${toolId}' 不能在当前选中对象上操作`);
|
||||
toolId = OperationType.DISABLED;
|
||||
}
|
||||
|
||||
// 保存先前的工具
|
||||
// 保存先前的工具
|
||||
this.previousTool = this.activeTool.value;
|
||||
|
||||
// 取消画布的选中状态
|
||||
this.canvas?.discardActiveObject();
|
||||
this.canvasManager?.layerManager?.updateLayersObjectsInteractivity?.();
|
||||
this.canvas?.renderAll();
|
||||
|
||||
if(toolId !== OperationType.DISABLED){
|
||||
this.canvas?.discardActiveObject();
|
||||
this.canvasManager?.layerManager?.updateLayersObjectsInteractivity?.();
|
||||
this.canvas?.renderAll();
|
||||
}
|
||||
// 隐藏文本编辑面板
|
||||
this.hideTextEditor();
|
||||
|
||||
@@ -374,7 +419,6 @@ export class ToolManager {
|
||||
}
|
||||
|
||||
// 设置工具特定的状态
|
||||
const tool = this.tools[toolId];
|
||||
if (tool && typeof tool.setup === "function") {
|
||||
console.log(`画布切换工具:${tool.name}(${toolId})`)
|
||||
this.canvas.toolId = toolId;
|
||||
@@ -424,7 +468,7 @@ export class ToolManager {
|
||||
|
||||
const currentTool = this.activeTool.value;
|
||||
const tool = this.tools[currentTool];
|
||||
|
||||
if(tool?.specialLayerDisabled && this.checkToolCanOperateSelectedObject()) return;
|
||||
// 根据当前工具设置selection状态
|
||||
if (currentTool === OperationType.SELECT) {
|
||||
this.canvas.selection = true;
|
||||
@@ -455,24 +499,19 @@ export class ToolManager {
|
||||
if (!this.canvas) return;
|
||||
this.canvas.isDrawingMode = false;
|
||||
this.canvas.selection = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前工具是否禁止操作当前选中的对象
|
||||
* @param {Boolean} isBrushTool 是否为画笔工具
|
||||
* @returns {Boolean} 是否可以切换
|
||||
*/
|
||||
checkToolCanOperateSelectedObject(isBrushTool = false) {
|
||||
checkToolCanOperateSelectedObject() {
|
||||
const layer = this.layerManager?.getActiveLayer();
|
||||
const isSpecialLayer = !!layer?.specialType;
|
||||
const isSpecialLayer = !!layer?.isPrintTrims || !!layer?.isPrintTrimsGroup;
|
||||
if (isSpecialLayer) {
|
||||
if(isBrushTool){
|
||||
this._disableBrushIndicator();
|
||||
}
|
||||
this._disableBrushIndicator();
|
||||
this.canvas.defaultCursor = "not-allowed";
|
||||
}
|
||||
console.log("===========",isSpecialLayer, this.canvas.defaultCursor);
|
||||
return isSpecialLayer;
|
||||
}
|
||||
|
||||
@@ -482,7 +521,7 @@ export class ToolManager {
|
||||
*/
|
||||
setupBrushTool() {
|
||||
if (!this.canvas) return;
|
||||
if (this.checkToolCanOperateSelectedObject(true)) return;
|
||||
if (this.checkToolCanOperateSelectedObject()) return;
|
||||
|
||||
this.canvas.isDrawingMode = true;
|
||||
this.canvas.selection = false;
|
||||
@@ -526,7 +565,7 @@ export class ToolManager {
|
||||
*/
|
||||
setupEraserTool() {
|
||||
if (!this.canvas) return;
|
||||
if (this.checkToolCanOperateSelectedObject(true)) return;
|
||||
if (this.checkToolCanOperateSelectedObject()) return;
|
||||
|
||||
this.canvas.isDrawingMode = true;
|
||||
this.canvas.selection = false;
|
||||
@@ -580,6 +619,7 @@ export class ToolManager {
|
||||
*/
|
||||
setupLassoTool() {
|
||||
if (!this.canvas) return;
|
||||
if (this.checkToolCanOperateSelectedObject()) return;
|
||||
|
||||
this.canvas.isDrawingMode = false;
|
||||
this.canvas.selection = false;
|
||||
@@ -661,6 +701,53 @@ export class ToolManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置部件选取工具
|
||||
*/
|
||||
setupPartTool() {
|
||||
if (!this.canvas) return;
|
||||
this.canvas.isDrawingMode = false;
|
||||
this.canvas.selection = false;
|
||||
|
||||
if (this.canvasManager && this.canvasManager.partManager) {
|
||||
this.canvasManager.partManager.setCurrentTool(OperationType.PART);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置部件选取工具--矩形
|
||||
*/
|
||||
setupPartRectangleTool() {
|
||||
if (!this.canvas) return;
|
||||
this.canvas.isDrawingMode = false;
|
||||
this.canvas.selection = true;
|
||||
if (this.canvasManager && this.canvasManager.partManager) {
|
||||
this.canvasManager.partManager.setCurrentTool(OperationType.PART_RECTANGLE);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置部件选取工具--画笔
|
||||
*/
|
||||
setupPartBrushTool() {
|
||||
if (!this.canvas) return;
|
||||
this.canvas.isDrawingMode = true;
|
||||
this.canvas.selection = false;
|
||||
if (this.canvasManager && this.canvasManager.partManager) {
|
||||
this.canvasManager.partManager.setCurrentTool(OperationType.PART_BRUSH);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置部件选取工具--橡皮擦
|
||||
*/
|
||||
setupPartEraserTool() {
|
||||
if (!this.canvas) return;
|
||||
this.canvas.isDrawingMode = false;
|
||||
this.canvas.selection = false;
|
||||
if (this.canvasManager && this.canvasManager.partManager) {
|
||||
this.canvasManager.partManager.setCurrentTool(OperationType.PART_ERASER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置波浪工具
|
||||
*/
|
||||
@@ -676,7 +763,7 @@ export class ToolManager {
|
||||
*/
|
||||
setupLiquifyTool() {
|
||||
if (!this.canvas || !this.layerManager) return;
|
||||
if (this.checkToolCanOperateSelectedObject(true)) return;
|
||||
if (this.checkToolCanOperateSelectedObject()) return;
|
||||
|
||||
this.canvas.isDrawingMode = false;
|
||||
this.canvas.selection = false;
|
||||
|
||||
@@ -1005,3 +1005,110 @@ export async function base64ToCanvas(base64, scale = 1, sr = false) {
|
||||
image.onerror = reject;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片边界跟踪算法(透明底)
|
||||
* @param {HTMLCanvasElement} canvas - canvas元素
|
||||
* @returns {Array} 边界点数组 [{x, y}, ...]
|
||||
*/
|
||||
export function traceImageContour(canvas) {
|
||||
const ctx = canvas.getContext("2d");
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const data = imageData.data;
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
|
||||
// 查找起始点(第一个不透明像素)
|
||||
let startX = -1;
|
||||
let startY = -1;
|
||||
|
||||
outer: for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
const index = (y * width + x) * 4;
|
||||
if (data[index + 3] > 0) {
|
||||
startX = x;
|
||||
startY = y;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (startX === -1) return []; // 没有不透明像素
|
||||
|
||||
// Moore-Neighbor边界跟踪算法
|
||||
const contour = [];
|
||||
const visited = new Set();
|
||||
const directions = [
|
||||
[-1, 0],
|
||||
[-1, -1],
|
||||
[0, -1],
|
||||
[1, -1],
|
||||
[1, 0],
|
||||
[1, 1],
|
||||
[0, 1],
|
||||
[-1, 1],
|
||||
];
|
||||
|
||||
let currentX = startX;
|
||||
let currentY = startY;
|
||||
let backtrackDir = 4; // 起始方向:右
|
||||
|
||||
do {
|
||||
const pointKey = `${currentX},${currentY}`;
|
||||
if (!visited.has(pointKey)) {
|
||||
contour.push({ x: currentX, y: currentY });
|
||||
visited.add(pointKey);
|
||||
}
|
||||
|
||||
// 从右方向开始顺时针查找
|
||||
let found = false;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const dir = (backtrackDir + i) % 8;
|
||||
const dx = directions[dir][0];
|
||||
const dy = directions[dir][1];
|
||||
const checkX = currentX + dx;
|
||||
const checkY = currentY + dy;
|
||||
|
||||
if (
|
||||
checkX >= 0 &&
|
||||
checkX < width &&
|
||||
checkY >= 0 &&
|
||||
checkY < height
|
||||
) {
|
||||
const index = (checkY * width + checkX) * 4;
|
||||
if (data[index + 3] > 0) {
|
||||
currentX = checkX;
|
||||
currentY = checkY;
|
||||
backtrackDir = (dir + 5) % 8; // 下一个开始查找的方向
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) break;
|
||||
} while (
|
||||
!(currentX === startX && currentY === startY) &&
|
||||
visited.size < width * height
|
||||
);
|
||||
|
||||
return contour;
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片添加gap转换
|
||||
* @param {HTMLCanvasElement} image - img元素
|
||||
* @param {Number} gapX - 水平gap
|
||||
* @param {Number} gapY - 垂直gap
|
||||
* @returns {HTMLCanvasElement} 转换后的canvas元素
|
||||
*/
|
||||
export function imageAddGapToCanvas(image, gapX, gapY) {
|
||||
// 创建透明 Canvas
|
||||
const tcanvas = document.createElement('canvas');
|
||||
tcanvas.width = image.width + gapX;
|
||||
tcanvas.height = image.height + gapY;
|
||||
const ctx = tcanvas.getContext('2d');
|
||||
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
|
||||
ctx.drawImage(image, 0, 0);
|
||||
return tcanvas;
|
||||
}
|
||||
|
||||
@@ -2191,3 +2191,27 @@ export const imageModeHandler = ({ imageMode, newImage, canvasWidth, canvasHeigh
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 调整图像大小
|
||||
* @param {string} base64 - 原始base64字符串
|
||||
* @param {number} width - 目标宽度
|
||||
* @param {number} height - 目标高度
|
||||
* @returns {Promise<string>} 处理后的base64字符串
|
||||
*/
|
||||
export const resizeImage = async (base64, width, height) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.src = base64;
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
resolve(canvas.toDataURL());
|
||||
};
|
||||
img.onerror = reject;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -25,18 +25,6 @@ export const SpecialLayerId = {
|
||||
SPECIAL_GROUP: "group_special", // 特殊组
|
||||
COLOR: "special_color", // 颜色图层
|
||||
}
|
||||
/**
|
||||
* 特殊类型
|
||||
*/
|
||||
export const SpecialType = {
|
||||
PRINT_TRIMS_G: "print_trims_group", // 印花和元素图层组
|
||||
PRINT_TRIMS_L: "print_trims_layer", // 印花和元素图层
|
||||
PRINT_TRIMS_O: "print_trims_object", // 印花和元素图层对象
|
||||
REPEAT_L: "repeat_layer",// 平铺图层
|
||||
REPEAT_O: "repeat_object",// 平铺图层对象
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@@ -44,6 +32,7 @@ export const SpecialType = {
|
||||
*/
|
||||
export const OperationType = {
|
||||
// 编辑器模式
|
||||
DISABLED: "disabled", // 禁用
|
||||
DRAW: "draw", // 绘画模式
|
||||
ERASER: "eraser", // 橡皮擦模式
|
||||
SELECT: "select", // 选择模式
|
||||
@@ -76,6 +65,12 @@ export const OperationType = {
|
||||
RED_BRUSH: "red_brush", // 红色笔刷
|
||||
GREEN_BRUSH: "green_brush", // 绿色笔刷
|
||||
|
||||
// 部件选取工具
|
||||
PART: "part", // 部件选取工具模式 - 点选模式
|
||||
PART_RECTANGLE: "part_rectangle", // 部件选取工具模式 - 矩形模式
|
||||
PART_BRUSH: "part_brush", // 部件选取工具模式 - 笔刷模式
|
||||
PART_ERASER: "part_eraser", // 部件选取工具模式 - 橡皮擦模式
|
||||
|
||||
// SHAPE: "shape", // 形状模式
|
||||
// 可以根据需要添加更多工具
|
||||
};
|
||||
|
||||
@@ -2,72 +2,122 @@
|
||||
<div class="demo">
|
||||
<div
|
||||
class="control"
|
||||
:class="{ active: item.id === activeId }"
|
||||
:class="{ active: item.token === activeToken }"
|
||||
v-for="(item, index) in list"
|
||||
:key="item.id"
|
||||
@click="onSelect(item.id)"
|
||||
:key="item.token"
|
||||
@click="onSelect(item.token)"
|
||||
>
|
||||
<div>
|
||||
<b>{{ item.name }}</b>
|
||||
<button
|
||||
v-if="index !== 0"
|
||||
@click="onMove(item.id, list[index - 1].id)"
|
||||
@click="onMove(item.token, list[index - 1].token)"
|
||||
>
|
||||
←
|
||||
</button>
|
||||
<button
|
||||
v-if="index !== list.length - 1"
|
||||
@click="onMove(item.id, list[index + 1].id)"
|
||||
@click="onMove(item.token, list[index + 1].token)"
|
||||
>
|
||||
→
|
||||
</button>
|
||||
<button @click.stop="onDelete(item.id)">删除</button>
|
||||
<button @click.stop="onDelete(item.token)">删除</button>
|
||||
</div>
|
||||
<div>
|
||||
<span>偏移X</span>
|
||||
<input type="number" v-model="item.location[0]" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="item.location[0]"
|
||||
@input="updateList(item, 'location[0]', item.location[0])"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>偏移Y</span>
|
||||
<input type="number" v-model="item.location[1]" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="item.location[1]"
|
||||
@input="updateList(item, 'location[1]', item.location[1])"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>角度</span>
|
||||
<input type="number" v-model="item.angle" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="item.angle"
|
||||
@input="updateList(item, 'angle', item.angle)"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>缩放</span>
|
||||
<input type="number" v-model="item.scale[0]" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="item.scale[0]"
|
||||
@input="updateList(item, 'scale[0]', item.scale[0])"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>水平间距</span>
|
||||
<input type="number" v-model="item.object.gapX" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="item.object.gapX"
|
||||
@input="updateList(item, 'object.gapX', item.object.gapX)"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>垂直间距</span>
|
||||
<input type="number" v-model="item.object.gapY" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="item.object.gapY"
|
||||
@input="updateList(item, 'object.gapY', item.object.gapY)"
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
<hr />
|
||||
<div>
|
||||
<span>缩放X</span>
|
||||
<input type="number" v-model="item.object.scaleX" step="0.1" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="item.object.scaleX"
|
||||
step="0.1"
|
||||
@input="
|
||||
updateList(item, 'object.scaleX', item.object.scaleX)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>缩放Y</span>
|
||||
<input type="number" v-model="item.object.scaleY" step="0.1" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="item.object.scaleY"
|
||||
step="0.1"
|
||||
@input="
|
||||
updateList(item, 'object.scaleY', item.object.scaleY)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>X</span>
|
||||
<input type="number" v-model="item.object.left" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="item.object.left"
|
||||
@input="updateList(item, 'object.left', item.object.left)"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>Y</span>
|
||||
<input type="number" v-model="item.object.top" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="item.object.top"
|
||||
@input="updateList(item, 'object.top', item.object.top)"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>角度</span>
|
||||
<input type="number" v-model="item.object.angle" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="item.object.angle"
|
||||
@input="updateList(item, 'object.angle', item.object.angle)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -78,6 +128,9 @@
|
||||
step="0.1"
|
||||
min="0"
|
||||
max="1"
|
||||
@input="
|
||||
updateList(item, 'object.opacity', item.object.opacity)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -86,6 +139,8 @@
|
||||
<pingpu
|
||||
:list="list"
|
||||
ref="pingpuRef"
|
||||
:width="600"
|
||||
:height="900"
|
||||
@change-canvas="updateCanvas"
|
||||
/>
|
||||
</div>
|
||||
@@ -105,10 +160,10 @@
|
||||
|
||||
const convertDotNotationToBracket = (str) =>
|
||||
str.replace(/(?:^|\.)(\d+)(?=\.|$)/g, "[#$1]").replace(/\[#/g, "[");
|
||||
const activeId = ref("1");
|
||||
const activeToken = ref("1");
|
||||
const list = ref([
|
||||
{
|
||||
id: "1",
|
||||
token: "1",
|
||||
ifSingle: false,
|
||||
level2Type: "Pattern",
|
||||
designType: "Library",
|
||||
@@ -133,7 +188,7 @@
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
token: "2",
|
||||
ifSingle: false,
|
||||
level2Type: "Pattern",
|
||||
designType: "Library",
|
||||
@@ -144,12 +199,12 @@
|
||||
name: "Print2",
|
||||
priority: 1,
|
||||
object: {
|
||||
top: 150,
|
||||
left: 250,
|
||||
top: 450,
|
||||
left: 300,
|
||||
scaleX: 0.5,
|
||||
scaleY: 0.5,
|
||||
opacity: 1,
|
||||
angle: 45,
|
||||
angle: 0,
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
blendMode: "multiply",
|
||||
@@ -163,42 +218,37 @@
|
||||
const oldList = ref(deepCopy(list.value));
|
||||
const pingpuRef = ref(null);
|
||||
const updateCanvas = (arr) => {
|
||||
console.log(arr);
|
||||
oldList.value = deepCopy(list.value);
|
||||
arr.forEach((item) => {
|
||||
list.value.forEach((v) => {
|
||||
const obj = list.value.find((v) => v.token === item.token);
|
||||
if (item.action === ACTIONS.UPDATE) {
|
||||
if (item.action === ACTIONS.UPDATE) {
|
||||
if (v.id === item.id) {
|
||||
if (item.action === ACTIONS.UPDATE) {
|
||||
try {
|
||||
const key = item.key;
|
||||
const str = /^\[/.test(item.key)
|
||||
? "v" + key
|
||||
: "v." + key;
|
||||
eval(`${str} = item.value`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
try {
|
||||
const key = item.key;
|
||||
const str = /^\[/.test(item.key)
|
||||
? "obj" + key
|
||||
: "obj." + key;
|
||||
eval(`${str} = item.value`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
} else if (item.action === ACTIONS.SELECT) {
|
||||
activeId.value = item.id;
|
||||
}
|
||||
});
|
||||
} else if (item.action === ACTIONS.SELECT) {
|
||||
activeToken.value = item.token;
|
||||
} else if (item.action === ACTIONS.DELETE) {
|
||||
list.value = list.value.filter((v) => v.token !== item.token);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onSelect = (id) => {
|
||||
activeId.value = id;
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
id: id,
|
||||
action: ACTIONS.SELECT,
|
||||
},
|
||||
]);
|
||||
const onSelect = (token) => {
|
||||
activeToken.value = token;
|
||||
pingpuRef.value.updataList([{ token, action: ACTIONS.SELECT }]);
|
||||
};
|
||||
const onMove = (id, id2) => {
|
||||
const obj1 = list.value.find((v) => v.id === id);
|
||||
const obj2 = list.value.find((v) => v.id === id2);
|
||||
const onMove = (token1, token2) => {
|
||||
const obj1 = list.value.find((v) => v.token === token1);
|
||||
const obj2 = list.value.find((v) => v.token === token2);
|
||||
const index1 = list.value.indexOf(obj1);
|
||||
const index2 = list.value.indexOf(obj2);
|
||||
if (index1 < index2) {
|
||||
@@ -206,26 +256,16 @@
|
||||
} else {
|
||||
list.value.splice(index1, 0, list.value.splice(index2, 1)[0]);
|
||||
}
|
||||
const ids = list.value.map((v) => v.id);
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
action: ACTIONS.SORT,
|
||||
ids,
|
||||
},
|
||||
]);
|
||||
const tokens = list.value.map((v) => v.token);
|
||||
pingpuRef.value.updataList([{ action: ACTIONS.SORT, tokens }]);
|
||||
};
|
||||
const onDelete = (id) => {
|
||||
list.value = list.value.filter((v) => v.id !== id);
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
id: id,
|
||||
action: ACTIONS.DELETE,
|
||||
},
|
||||
]);
|
||||
const onDelete = (token) => {
|
||||
list.value = list.value.filter((v) => v.token !== token);
|
||||
pingpuRef.value.updataList([{ token, action: ACTIONS.DELETE }]);
|
||||
};
|
||||
const onAdd = () => {
|
||||
const obj = {
|
||||
id: Date.now().toString(),
|
||||
token: Date.now().toString(),
|
||||
ifSingle: false,
|
||||
level2Type: "Pattern",
|
||||
designType: "Library",
|
||||
@@ -250,59 +290,20 @@
|
||||
},
|
||||
};
|
||||
list.value.push(obj);
|
||||
pingpuRef.value.updataList([{ action: ACTIONS.ADD, data: obj }]);
|
||||
};
|
||||
// 监听列表变化属性变更
|
||||
const updateList = (item, key, value) => {
|
||||
if (key === "scale[0]") item.scale[1] = value;
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
action: ACTIONS.ADD,
|
||||
data: obj,
|
||||
token: item.token,
|
||||
action: ACTIONS.UPDATE,
|
||||
key,
|
||||
value,
|
||||
},
|
||||
]);
|
||||
};
|
||||
watch(
|
||||
() => list.value,
|
||||
() => updateList(),
|
||||
{ deep: true }
|
||||
);
|
||||
// 监听列表变化属性变更
|
||||
const updateList = () => {
|
||||
const changeList = [];
|
||||
oldList.value.forEach((oldItem) => {
|
||||
const newItem = list.value.find((v) => v.id === oldItem.id);
|
||||
if (newItem) {
|
||||
const arr = findDiffProps(oldItem, newItem);
|
||||
arr.forEach((item) => {
|
||||
changeList.push({
|
||||
...item,
|
||||
id: oldItem.id,
|
||||
action: ACTIONS.UPDATE,
|
||||
key: convertDotNotationToBracket(item.key),
|
||||
});
|
||||
});
|
||||
} else {
|
||||
changeList.push({
|
||||
id: oldItem.id,
|
||||
action: ACTIONS.DELETE,
|
||||
});
|
||||
}
|
||||
});
|
||||
oldList.value = deepCopy(list.value);
|
||||
if (changeList.length) {
|
||||
pingpuRef.value.updataList(changeList);
|
||||
}
|
||||
};
|
||||
// 递归查找不同的属性
|
||||
const findDiffProps = (obj1, obj2, diffProps = [], path = "") => {
|
||||
for (const key in obj1) {
|
||||
if (typeof obj1[key] === "object") {
|
||||
findDiffProps(obj1[key], obj2[key], diffProps, `${path}${key}.`);
|
||||
} else if (obj1[key] !== obj2[key]) {
|
||||
diffProps.push({
|
||||
key: `${path}${key}`,
|
||||
value: obj2[key],
|
||||
});
|
||||
}
|
||||
}
|
||||
return diffProps;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="pingpu" ref="el"><canvas ref="canvasRef"></canvas></div>
|
||||
<div class="overall-canvas" ref="el"><canvas ref="canvasRef"></canvas></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -30,9 +30,11 @@
|
||||
DELETE: "delete",
|
||||
SORT: "sort",
|
||||
};
|
||||
const emit = defineEmits(["change-canvas"]);
|
||||
const emit = defineEmits(["change-canvas", "init-canvas"]);
|
||||
const props = defineProps({
|
||||
list: { type: Array, default: () => [] },
|
||||
width: { type: Number, required: true },
|
||||
height: { type: Number, required: true },
|
||||
});
|
||||
const el = ref(null);
|
||||
const canvasRef = ref(null);
|
||||
@@ -41,7 +43,6 @@
|
||||
onMounted(async () => {
|
||||
initCanvas();
|
||||
await setCanvasData();
|
||||
|
||||
let throttleTimeout = null;
|
||||
let lastRunTime = 0;
|
||||
let trailingTimeout = null;
|
||||
@@ -63,6 +64,7 @@
|
||||
}
|
||||
});
|
||||
observer.value.observe(el.value);
|
||||
emit("init-canvas", props.list);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
observer.value.disconnect();
|
||||
@@ -100,23 +102,23 @@
|
||||
const object = e.target;
|
||||
const action = e.action;
|
||||
const list = [];
|
||||
const id = object.id;
|
||||
const token = object.token;
|
||||
if (action === "drag" || action === "rotate") {
|
||||
list.push({
|
||||
id: id,
|
||||
token,
|
||||
action: ACTIONS.UPDATE,
|
||||
key: KEYS.O_TOP,
|
||||
value: object.top,
|
||||
value: (props.height / canvas.height) * object.top,
|
||||
});
|
||||
list.push({
|
||||
id: id,
|
||||
token,
|
||||
action: ACTIONS.UPDATE,
|
||||
key: KEYS.O_LEFT,
|
||||
value: object.left,
|
||||
value: (props.width / canvas.width) * object.left,
|
||||
});
|
||||
if (action === "rotate") {
|
||||
list.push({
|
||||
id: id,
|
||||
token,
|
||||
action: ACTIONS.UPDATE,
|
||||
key: KEYS.O_ANGLE,
|
||||
value: object.angle,
|
||||
@@ -124,13 +126,13 @@
|
||||
}
|
||||
} else if (action === "scale") {
|
||||
list.push({
|
||||
id: id,
|
||||
token,
|
||||
action: ACTIONS.UPDATE,
|
||||
key: KEYS.O_SCALE_X,
|
||||
value: object.scaleX,
|
||||
});
|
||||
list.push({
|
||||
id: id,
|
||||
token,
|
||||
action: ACTIONS.UPDATE,
|
||||
key: KEYS.O_SCALE_Y,
|
||||
value: object.scaleY,
|
||||
@@ -140,15 +142,17 @@
|
||||
};
|
||||
// 对象选中
|
||||
const onObjectSelected = (e) => {
|
||||
const id = e.selected[0].id;
|
||||
const list = [
|
||||
{
|
||||
id: id,
|
||||
action: ACTIONS.SELECT,
|
||||
},
|
||||
];
|
||||
const token = e.selected[0].token;
|
||||
const list = [{ token, action: ACTIONS.SELECT }];
|
||||
emit("change-canvas", list);
|
||||
};
|
||||
// 删除对象
|
||||
const onDeleteItem = (object) => {
|
||||
const list = [{ token: object.token, action: ACTIONS.DELETE }];
|
||||
emit("change-canvas", list);
|
||||
canvas.remove(object);
|
||||
canvas.renderAll();
|
||||
};
|
||||
const urlToCanvas = (url) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fabric.Image.fromURL(
|
||||
@@ -181,17 +185,19 @@
|
||||
const cheight = canvas.height;
|
||||
let pattern = await setFill(item);
|
||||
let rect = new fabric.Rect({
|
||||
id: item.id,
|
||||
token: item.token,
|
||||
width: cwidth,
|
||||
height: cheight,
|
||||
fill: pattern,
|
||||
...item.object,
|
||||
top: item.object.top / (props.height / canvas.height),
|
||||
left: item.object.left / (props.width / canvas.width),
|
||||
onDelete: (v) => onDeleteItem(v),
|
||||
});
|
||||
canvas.add(rect);
|
||||
};
|
||||
const setFill = async (item) => {
|
||||
if (!item) return null;
|
||||
console.log(item.scale);
|
||||
const cwidth = canvas.width;
|
||||
const cheight = canvas.height;
|
||||
let image = await urlToCanvas(item.path);
|
||||
@@ -225,19 +231,20 @@
|
||||
return pattern;
|
||||
};
|
||||
const updataList = async (list) => {
|
||||
console.log(list);
|
||||
const objects = canvas.getObjects();
|
||||
// list.forEach((item) => {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i];
|
||||
let object = objects.find((o) => o.id === item.id);
|
||||
let object = objects.find((o) => o.token === item.token);
|
||||
if (item.action === ACTIONS.UPDATE) {
|
||||
if (!object) continue;
|
||||
let value = item.value;
|
||||
switch (item.key) {
|
||||
case KEYS.O_TOP:
|
||||
object.set("top", value);
|
||||
object.set("top", value / (props.height / canvas.height));
|
||||
break;
|
||||
case KEYS.O_LEFT:
|
||||
object.set("left", value);
|
||||
object.set("left", value / (props.width / canvas.width));
|
||||
break;
|
||||
case KEYS.O_OPACITY:
|
||||
object.set("opacity", value);
|
||||
@@ -268,24 +275,23 @@
|
||||
case KEYS.FILL_GAPX:
|
||||
case KEYS.FILL_GAPY:
|
||||
let pattern = await setFill(
|
||||
props.list.find((v) => v.id === item.id)
|
||||
props.list.find((v) => v.token === item.token)
|
||||
);
|
||||
object.set("fill", pattern);
|
||||
break;
|
||||
}
|
||||
} else if (item.action === ACTIONS.SELECT) {
|
||||
canvas.setActiveObject(object);
|
||||
if (object) canvas.setActiveObject(object);
|
||||
} else if (item.action === ACTIONS.SORT) {
|
||||
let ids = item.ids;
|
||||
let tokens = item.tokens;
|
||||
canvas.clear();
|
||||
for (let j = 0; j < ids.length; j++) {
|
||||
let id = ids[j];
|
||||
let object = objects.find((o) => o.id === id);
|
||||
canvas.add(object);
|
||||
for (let j = 0; j < tokens.length; j++) {
|
||||
let object = objects.find((o) => o.token === tokens[j]);
|
||||
if (object) canvas.add(object);
|
||||
}
|
||||
canvas.renderAll();
|
||||
} else if (item.action === ACTIONS.DELETE) {
|
||||
canvas.remove(object);
|
||||
if (object) canvas.remove(object);
|
||||
} else if (item.action === ACTIONS.ADD) {
|
||||
await addObject(item.data);
|
||||
}
|
||||
@@ -298,7 +304,7 @@
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
.pingpu {
|
||||
.overall-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
<template>
|
||||
<div class="test" ref="testRef">
|
||||
<!-- <div class="canvas-container">
|
||||
<!-- <div class="test" ref="testRef">
|
||||
<div class="canvas-container">
|
||||
<canvas id="canvas"></canvas>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<overall-canvas-demo />
|
||||
<div style="width: 100px; height: 100px;position: relative;"><CanvasEditor /></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { fabric } from "fabric-with-all";
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import CanvasEditor from "./CanvasEditor/index.vue";
|
||||
import OverallCanvasDemo from "./OverallCanvas/demo.vue";
|
||||
const imageUrl = "/src/assets/images/canvas/xiangaofenge.png";
|
||||
const testRef = ref(null);
|
||||
|
||||
@@ -35,53 +39,104 @@
|
||||
canvas1.height = height;
|
||||
const ctx1 = canvas1.getContext("2d");
|
||||
ctx1.drawImage(image, 0, 0, width, height);
|
||||
const data = ctx1.getImageData(0, 0, width, height);
|
||||
testRef.value.appendChild(canvas1);
|
||||
const testData = test(data);
|
||||
const canvas2 = document.createElement("canvas");
|
||||
canvas2.width = width;
|
||||
canvas2.height = height;
|
||||
const ctx2 = canvas2.getContext("2d");
|
||||
ctx2.putImageData(testData, 0, 0);
|
||||
testRef.value.appendChild(canvas2);
|
||||
const arr = traceImageContour(canvas1);
|
||||
const str = arr.map((v) => `${v.x} ${v.y}`).join(" L ");
|
||||
const path = new fabric.Path(`M ${str} z`);
|
||||
path.set({
|
||||
fill: "rgba(127, 255, 127, 0.3)",
|
||||
stroke: "#2AA81B",
|
||||
strokeWidth: 2,
|
||||
strokeDashArray: [8, 4],
|
||||
strokeLineCap: "round",// 折线端点样式
|
||||
strokeLineJoin: "bevel", // 折线连接样式
|
||||
strokeUniform: true, // 保持描边宽度不随缩放改变
|
||||
});
|
||||
canvas.add(path);
|
||||
};
|
||||
});
|
||||
// 获取图片轮廓点位
|
||||
function test(data) {
|
||||
// 找过的点位
|
||||
const visited = [];
|
||||
// 轮廓点位
|
||||
const contours = [];
|
||||
const { width, height } = data;
|
||||
function cd(x, y) {
|
||||
const arr = [
|
||||
[x, y], // 当前
|
||||
[x, y - 1], // 上
|
||||
[x + 1, y], // 右
|
||||
[x, y + 1], // 下
|
||||
[x - 1, y], // 左
|
||||
];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
let [x1, y1] = arr[i];
|
||||
if (x1 < 0 || x1 >= width || y1 < 0 || y1 >= height) continue;
|
||||
let key = `${x1},${y1}`;
|
||||
if (visited.includes(key)) continue;
|
||||
visited.push(key);
|
||||
let index = (y1 * width + x1) * 4;
|
||||
let r = data.data[index];
|
||||
let g = data.data[index + 1];
|
||||
let b = data.data[index + 2];
|
||||
let a = data.data[index + 3];
|
||||
if ((r || g || b) && a) {
|
||||
contours.push({ x: x1, y: y1 });
|
||||
} else {
|
||||
if (i > 0) cd(x1, y1);
|
||||
// 边界追踪
|
||||
function traceImageContour(canvas) {
|
||||
const ctx = canvas.getContext("2d");
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const data = imageData.data;
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
|
||||
// 查找起始点(第一个不透明像素)
|
||||
let startX = -1;
|
||||
let startY = -1;
|
||||
|
||||
outer: for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
const index = (y * width + x) * 4;
|
||||
if (data[index + 3] > 0) {
|
||||
startX = x;
|
||||
startY = y;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
cd(0, 0);
|
||||
console.log(contours);
|
||||
return data;
|
||||
|
||||
if (startX === -1) return []; // 没有不透明像素
|
||||
|
||||
// Moore-Neighbor边界跟踪算法
|
||||
const contour = [];
|
||||
const visited = new Set();
|
||||
const directions = [
|
||||
[-1, 0],
|
||||
[-1, -1],
|
||||
[0, -1],
|
||||
[1, -1],
|
||||
[1, 0],
|
||||
[1, 1],
|
||||
[0, 1],
|
||||
[-1, 1],
|
||||
];
|
||||
|
||||
let currentX = startX;
|
||||
let currentY = startY;
|
||||
let backtrackDir = 4; // 起始方向:右
|
||||
|
||||
do {
|
||||
const pointKey = `${currentX},${currentY}`;
|
||||
if (!visited.has(pointKey)) {
|
||||
contour.push({ x: currentX, y: currentY });
|
||||
visited.add(pointKey);
|
||||
}
|
||||
|
||||
// 从右方向开始顺时针查找
|
||||
let found = false;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const dir = (backtrackDir + i) % 8;
|
||||
const dx = directions[dir][0];
|
||||
const dy = directions[dir][1];
|
||||
const checkX = currentX + dx;
|
||||
const checkY = currentY + dy;
|
||||
|
||||
if (
|
||||
checkX >= 0 &&
|
||||
checkX < width &&
|
||||
checkY >= 0 &&
|
||||
checkY < height
|
||||
) {
|
||||
const index = (checkY * width + checkX) * 4;
|
||||
if (data[index + 3] > 0) {
|
||||
currentX = checkX;
|
||||
currentY = checkY;
|
||||
backtrackDir = (dir + 5) % 8; // 下一个开始查找的方向
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) break;
|
||||
} while (
|
||||
!(currentX === startX && currentY === startY) &&
|
||||
visited.size < width * height
|
||||
);
|
||||
|
||||
return contour;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="item detailLeft" :class="{isEditPattern:isEditPattern.value}">
|
||||
<detailLeft v-if="currentDetailType" ref="detailLeft"></detailLeft>
|
||||
<detailLeft v-if="currentDetailType" ref="detailLeft" :detailLeftColorKey="detailLeftColorKey"></detailLeft>
|
||||
<!-- <detailLeft v-if="selectDetail && selectDetail.id && currentDetailType"></detailLeft> -->
|
||||
<div class="btn" style="margin: 0;" v-show="currentDetailType == 'color'">
|
||||
<div class="gallery_btn" @click="previwe">{{$t('DesignPrintOperation.Preview')}}</div>
|
||||
@@ -61,13 +61,10 @@
|
||||
<model
|
||||
ref="model"
|
||||
:key="positionKey"
|
||||
@addDetail="addDetail"
|
||||
@canvasReload="canvasReload"
|
||||
@detailEdit="detailEdit"
|
||||
@addSketch="()=>isEditPattern.value = ''"
|
||||
@revocation="revocation"
|
||||
@oppositeRevocation="oppositeRevocation"
|
||||
@modelOnLoad="modelOnLoad"
|
||||
@sketchSysToLibrary="sketchSysToLibrary"
|
||||
></model>
|
||||
<div class="btn">
|
||||
@@ -88,7 +85,7 @@
|
||||
" alt="">
|
||||
</div>
|
||||
<div class="contentRight" v-else-if="currentDetailType && !isEditPattern.value">
|
||||
<detailRight ref="detailRight"></detailRight>
|
||||
<detailRight ref="detailRight" :sketchSize="sketchSize"></detailRight>
|
||||
<div class="btn"
|
||||
v-show="
|
||||
currentDetailType !== 'color' &&
|
||||
@@ -103,15 +100,14 @@
|
||||
<div class="gallery_btn" @click="previwe">{{$t('DesignPrintOperation.Preview')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contentRight" v-if="selectDetail && selectDetail.id && currentDetailType && isEditPattern.value">
|
||||
<canvasBox ref="canvasBox" :key="canvasKey || isEditPattern.value" :isEditPattern="isEditPattern.value"></canvasBox>
|
||||
<div class="contentRight canvas" v-if="selectDetail && selectDetail.id && currentDetailType" :class="{'active': isEditPattern.value}">
|
||||
<canvasBox ref="canvasBox" :key="canvasKey" :sketchSize="sketchSize" @setSloganData="setSloganData" :isEditPattern="isEditPattern.value" :updateOtherLayers="updateOtherLayers"></canvasBox>
|
||||
</div>
|
||||
<!-- 画布 -->
|
||||
<!-- <div class="content" v-else-if="selectDetail && selectDetail.id">
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
<addDetails ref="addDetails" @setSloganData="setSloganData"></addDetails>
|
||||
</a-modal>
|
||||
<div class="mark_loading" v-show="loadingShow">
|
||||
<a-spin size="large" />
|
||||
@@ -130,14 +126,13 @@ import canvasBox from './canvas/index.vue'
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { Https } from "@/tool/https";
|
||||
import { Modal,message } from 'ant-design-vue';
|
||||
import {getUploadUrl,isMoible,setGradual} from '@/tool/util'
|
||||
import {getUploadUrl,segmentImage,setGradual,rgbToHsv,rgbaToHex} from '@/tool/util'
|
||||
import { useStore } from "vuex";
|
||||
import { openGuide,driverObj__ } from "@/tool/guide";
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import addDetails from '@/component/Detail/addDetails.vue'
|
||||
export default defineComponent({
|
||||
components:{
|
||||
detailLeft,model,detailRight,canvasBox,addDetails
|
||||
detailLeft,model,detailRight,canvasBox
|
||||
},
|
||||
emits:['destroy'],
|
||||
setup(props,{emit}) {
|
||||
@@ -148,7 +143,6 @@ export default defineComponent({
|
||||
canvasBox,
|
||||
detailRight,
|
||||
detailLeft:null as any,
|
||||
addDetails:null as any,
|
||||
})
|
||||
const userDetail = computed(()=>{
|
||||
return store.state.UserHabit.userDetail
|
||||
@@ -157,10 +151,12 @@ export default defineComponent({
|
||||
selectObject:computed(()=>store.state.Workspace.probjects) as any,//选择的项目
|
||||
designDetail:computed(()=>store.state.DesignDetail.designDetail),
|
||||
currentDetailType:computed(()=>store.state.DesignDetail.currentDetailType),
|
||||
frontBack:computed(()=>store.state.DesignDetail.frontBack),
|
||||
selectDetail:computed(()=>store.state.DesignDetail.selectDetail),
|
||||
designDetailShow:false,
|
||||
loadingShow:false,
|
||||
oppositeRevocationShow:-1,
|
||||
imgDomIndex:-1,
|
||||
revocationShow:-1,
|
||||
isEditPattern:{
|
||||
value:'' as any,
|
||||
@@ -173,10 +169,30 @@ export default defineComponent({
|
||||
fun:null,
|
||||
},
|
||||
positionKey:0,
|
||||
isUndividedLayerWithSinglePrint:false,
|
||||
detailLeftColorKey:0,
|
||||
sketchSize:{
|
||||
width:0,
|
||||
height:0,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const getSketchSize:any = async ()=>{
|
||||
let img = new Image();
|
||||
img.src = detailData.selectDetail.path;
|
||||
await new Promise((resolve, reject) => {
|
||||
img.onload = () => {
|
||||
detailData.sketchSize.width = img.width
|
||||
detailData.sketchSize.height = img.height
|
||||
resolve([img.width, img.height]);
|
||||
}
|
||||
img.onerror = reject;
|
||||
});
|
||||
}
|
||||
watch(()=>detailData.selectDetail,async (newValue,oldValue)=>{
|
||||
detailData.imgDomIndex = detailData.frontBack.front.findIndex((item:any)=>item.id == newValue.id)
|
||||
if(newValue)await getSketchSize()
|
||||
detailData.canvasKey += 1
|
||||
// privewDetail(oldValue)
|
||||
},{immediate: true})
|
||||
provide('getCanvasIfEdit',detailData.getCanvasIfEdit)
|
||||
provide('singleOveral',detailData.singleOveral)
|
||||
provide('isEditPattern',detailData.isEditPattern)
|
||||
@@ -195,6 +211,11 @@ export default defineComponent({
|
||||
detailData.loadingShow = true
|
||||
Https.axiosGet(url).then(
|
||||
async (rv: any) => {
|
||||
//清除画布JSON数据
|
||||
sessionStorage.removeItem('canvasList');
|
||||
sessionStorage.removeItem('revocation');
|
||||
sessionStorage.removeItem('oppositeRevocation');
|
||||
sessionStorage.setItem('key', 'value');
|
||||
store.commit('DesignDetail/setDesignDetail',rv)
|
||||
rv.clothes.forEach((item:any)=>{
|
||||
let a
|
||||
@@ -228,25 +249,17 @@ export default defineComponent({
|
||||
element.designType = 'Library'
|
||||
}
|
||||
});
|
||||
if(!item.layersObject[0].undividedLayerWithSinglePrint){
|
||||
detailData.isUndividedLayerWithSinglePrint = true
|
||||
}
|
||||
})
|
||||
detailData.singleOveral.value = rv.singleOverall
|
||||
detailData.designDetailShow = true
|
||||
// this.deleteShow = false
|
||||
initialize()
|
||||
setRevocation()
|
||||
if(rv.singleOverall == "single"){
|
||||
store.commit('DesignDetail/setDesignColthes',rv.clothes[0].id)
|
||||
modelOnLoad()
|
||||
}
|
||||
|
||||
|
||||
if(detailData.isUndividedLayerWithSinglePrint){
|
||||
}else{
|
||||
detailData.loadingShow = false
|
||||
}
|
||||
detailData.loadingShow = false
|
||||
resolve(rv)
|
||||
}
|
||||
).catch(rv=>{
|
||||
@@ -256,12 +269,6 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
}
|
||||
const initialize = ()=>{//design后初始化
|
||||
|
||||
|
||||
sessionStorage.removeItem('oppositeRevocation')
|
||||
sessionStorage.removeItem('revocation')
|
||||
}
|
||||
//撤回
|
||||
const setRevocation = ()=>{//设置撤销
|
||||
let itemDetail = JSON.parse(JSON.stringify(detailData.designDetail))
|
||||
@@ -320,13 +327,15 @@ export default defineComponent({
|
||||
const setCurrentDetail = (str:string)=>{
|
||||
store.commit('DesignDetail/setCurrentDetailType',str)
|
||||
}
|
||||
const setClothes = async (list:any)=>{
|
||||
const setClothes = async (list:any,str:string)=>{
|
||||
let clothesList:any = []
|
||||
await nextTick()
|
||||
if(detailData.isEditPattern.value == 'editSketch')await detailDom.canvasBox.submitBase64Data().then((rv)=>{
|
||||
detailData.selectDetail.sketchString = rv
|
||||
})
|
||||
for(let i = 0;i<list.length;i++){
|
||||
detailData.selectDetail
|
||||
let {scale,offset,priority,maskUrl,maskMinioUrl} = await (detailDom.model as any).getSubmitData(list[i],detailData.isUndividedLayerWithSinglePrint)
|
||||
if(detailDom.canvasBox?.privewDetail)await (detailDom.canvasBox as any).privewDetail()
|
||||
let {scale,offset,priority,transpose,rotate,maskUrl,maskMinioUrl} = await (detailDom.model as any).getSubmitData(list[i])
|
||||
if(detailDom.detailRight?.privewDetail)await (detailDom.detailRight as any).privewDetail()
|
||||
let gradient = null
|
||||
let newData = list[i]?.newDetail?.[detailData.currentDetailType]
|
||||
@@ -349,6 +358,32 @@ export default defineComponent({
|
||||
detailData.selectDetail.maskUrl = ''
|
||||
detailData.selectDetail.maskMinioUrl = ''
|
||||
}
|
||||
function isNetworkPath(str) {
|
||||
if (typeof str !== 'string' || str.trim() === '') {
|
||||
return false;
|
||||
}
|
||||
const urlPatterns = [
|
||||
/^https?:\/\//i, // http:// 或 https://
|
||||
/^ftp:\/\//i, // ftp://
|
||||
/^ws:\/\//i, // ws://
|
||||
/^wss:\/\//i, // wss://
|
||||
/^\/\//, // 协议相对路径 //example.com
|
||||
/^data:/i, // data:image/png;base64,...(这是Base64,不是网络路径)
|
||||
];
|
||||
// 排除data:URL(这实际上是Base64)
|
||||
if (str.startsWith('data:')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return urlPatterns.some(pattern => pattern.test(str));
|
||||
}
|
||||
const printObjectToJSON = (list:any)=>{
|
||||
if(list?.length > 0){
|
||||
list.forEach((item:any)=>{
|
||||
if(item.object)item.object = JSON.stringify(item.object)
|
||||
})
|
||||
}
|
||||
}
|
||||
let data:any = {
|
||||
changed:false,
|
||||
color,
|
||||
@@ -356,13 +391,15 @@ export default defineComponent({
|
||||
id:(newData && detailData.currentDetailType == 'sketch' && isCurrent && !detailData.isEditPattern.value)?newData.id:list[i].id,
|
||||
maskMinioUrl:((newData && detailData.currentDetailType == 'sketch') || list[i].sketchString)?'':list[i]?.maskMinioUrl,
|
||||
// maskUrl:'',
|
||||
maskUrl:((newData && detailData.currentDetailType == 'sketch') || list[i].sketchString)?'':list[i]?.maskUrl,
|
||||
maskUrl:list[i]?.maskUrl || '',
|
||||
// offset:[
|
||||
// -233.13985,
|
||||
// 406.90964
|
||||
// ],
|
||||
offset,
|
||||
partialDesign:list[i].partialDesign || {},
|
||||
transpose,
|
||||
rotate,
|
||||
partialDesign:list[i].partialDesign,
|
||||
// partialDesign:detailData.isEditPattern.value?list[i].partialDesign:{},
|
||||
path:(newData && detailData.currentDetailType == 'sketch' && isCurrent && !detailData.isEditPattern.value)?newData.minIOPath:list[i].minIOPath,
|
||||
printObject:(newData && detailData.currentDetailType == 'print' && isCurrent && !detailData.isEditPattern.value)?{prints:newData}:list[i].printObject?list[i].printObject:{prints:[]},
|
||||
@@ -377,6 +414,8 @@ export default defineComponent({
|
||||
trims:(newData && detailData.currentDetailType == 'element' && isCurrent && !detailData.isEditPattern.value)?{prints:newData}:list[i].trims?.prints?list[i].trims:{prints:[]},
|
||||
accessory:(newData && detailData.currentDetailType == 'accessory' && isCurrent && !detailData.isEditPattern.value)?{prints:newData}:list[i].trims?.prints?list[i].trims:{prints:[]},
|
||||
}
|
||||
data.printObject.prints = printObjectToJSON(data.printObject.prints)
|
||||
data.trims.prints = printObjectToJSON(data.trims.prints)
|
||||
if((detailData.isEditPattern.value && list[i].color?.gradient) || (!detailData.isEditPattern.value && (list[i].newDetail?.color?.gradient || list[i].color?.gradient))){
|
||||
gradient = list[i].newDetail?.color?.gradient || list[i].color.gradient
|
||||
gradient.colorImg = await setGradual(gradient,320,700)
|
||||
@@ -391,10 +430,10 @@ export default defineComponent({
|
||||
let workspace = store.state.Workspace.probjects
|
||||
if(!detailData?.selectDetail?.path && !detailData?.selectDetail?.newDetail?.sketch?.minIOPath)return
|
||||
let clothes:any
|
||||
if(detailData.currentDetailType == 'models' || detailData.isUndividedLayerWithSinglePrint){
|
||||
clothes = await setClothes(detailData.designDetail.clothes)
|
||||
if(detailData.currentDetailType == 'models'){
|
||||
clothes = await setClothes(detailData.designDetail.clothes,str)
|
||||
}else{
|
||||
clothes = await setClothes([detailData.selectDetail])
|
||||
clothes = await setClothes([detailData.selectDetail],str)
|
||||
}
|
||||
let data = {
|
||||
designItemId:detailData.designDetail.designItemId,
|
||||
@@ -405,6 +444,7 @@ export default defineComponent({
|
||||
sketchString:'',
|
||||
modelId:(detailData.currentDetailType == 'models' && detailData.designDetail.newModel)?detailData.designDetail.newModel.id:detailData.designDetail.oldModel?detailData.designDetail.oldModel.id:'',
|
||||
modelType:(detailData.currentDetailType == 'models' && detailData.designDetail.newModel)?detailData.designDetail.newModel.type:detailData.designDetail.oldModel?detailData.designDetail.oldModel.type:'',
|
||||
designType:detailData.selectDetail.id?'merage':'default',
|
||||
timeZone:Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
processId:userDetail.value.userId,
|
||||
probjectId:store.state.Workspace.probjects.id,
|
||||
@@ -417,10 +457,10 @@ export default defineComponent({
|
||||
fun:setRevocation
|
||||
}
|
||||
if(detailData?.designDetail?.newModel)detailData.designDetail.oldModel = JSON.parse(JSON.stringify(detailData.designDetail.newModel))
|
||||
delete detailData.designDetail.newModel
|
||||
// delete detailData.designDetail.newModel
|
||||
detailData.selectDetail.sketchString = null
|
||||
store.commit('DesignDetail/setPraeview',value)
|
||||
detailData.loadingShow = false
|
||||
detailData.isUndividedLayerWithSinglePrint = false
|
||||
canvasReload()
|
||||
// setRevocation()
|
||||
}).catch(res=>{
|
||||
@@ -429,7 +469,7 @@ export default defineComponent({
|
||||
}
|
||||
const submit = async ()=>{
|
||||
let workspace = store.state.Workspace.probjects
|
||||
let clothes:any = await setClothes(detailData.designDetail.clothes)
|
||||
let clothes:any = await setClothes(detailData.designDetail.clothes,'sub')
|
||||
let data = {
|
||||
designItemId:detailData.designDetail.designItemId,
|
||||
designSingleItemDTOList:clothes,
|
||||
@@ -439,13 +479,21 @@ export default defineComponent({
|
||||
sketchString:'',
|
||||
modelId:(detailData.currentDetailType == 'models' && detailData.designDetail.newModel)?detailData.designDetail.newModel.id:detailData.designDetail.oldModel?detailData.designDetail.oldModel.id:'',
|
||||
modelType:(detailData.currentDetailType == 'models' && detailData.designDetail.newModel)?detailData.designDetail.newModel.type:detailData.designDetail.oldModel?detailData.designDetail.oldModel.type:'',
|
||||
designType:detailData.selectDetail.id?'merage':'default',
|
||||
timeZone:Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
processId:userDetail.value.userId,
|
||||
probjectId:store.state.Workspace.probjects.id,
|
||||
}
|
||||
detailData.loadingShow = true
|
||||
Https.axiosPost(Https.httpUrls.designSingle, data).then((rv)=>{
|
||||
Https.axiosPost(Https.httpUrls.designSingle, data).then(async (rv)=>{
|
||||
saveCanvasJSONToSession()
|
||||
// store.commit('DesignDetail/setPraeview',rv)
|
||||
const sessionCanvasList = sessionStorage.getItem('canvasList');
|
||||
const canvasList = sessionCanvasList ? JSON.parse(sessionCanvasList) : []
|
||||
for (let i = 0; i < canvasList.length; i++) {
|
||||
const index = detailData.designDetail.clothes.findIndex(item => item.id === canvasList[i].id);
|
||||
if(index>-1)await detailDom.canvasBox.saveCanvas(canvasList[i])
|
||||
}
|
||||
let designCollectionList = store.state.HomeStoreModule.designCollectionList
|
||||
let likeDesignCollectionList = store.state.HomeStoreModule.likeDesignCollectionList
|
||||
designCollectionList.forEach((item:any) => {
|
||||
@@ -466,45 +514,191 @@ export default defineComponent({
|
||||
detailData.loadingShow = false
|
||||
});
|
||||
}
|
||||
const previwe = ()=>{
|
||||
let data = getSubmitData('preview')
|
||||
store.dispatch('DesignDetail/setSubmit',data)
|
||||
}
|
||||
const modelOnLoad = ()=>{
|
||||
if(!detailData.isUndividedLayerWithSinglePrint)return
|
||||
setTimeout(()=>{
|
||||
let data = getSubmitData('sub')
|
||||
const previwe = async ()=>{
|
||||
if((detailData.currentDetailType == 'sketch' && !detailData.isEditPattern.value) || detailData.isEditPattern.value == 'editSketch'){
|
||||
let data = getSubmitData('preview')
|
||||
store.dispatch('DesignDetail/setSubmit',data)
|
||||
}else{
|
||||
//走画布合成图片并且直接分割
|
||||
if(detailData.isEditPattern.value !== 'canvasEditor'){
|
||||
if(detailDom.detailRight?.privewDetail)await (detailDom.detailRight as any).privewDetail()
|
||||
let otherData = await updateOtherLayers('single')
|
||||
await detailDom.canvasBox.updateOtherLayers(otherData)
|
||||
}
|
||||
await detailDom.canvasBox.privewDetail()
|
||||
let img = new Image()
|
||||
img.onload = ()=>{
|
||||
let partialDesign = detailData.selectDetail.partialDesign.partialDesignBase64 || detailData.selectDetail.partialDesign.partialDesignPath
|
||||
let size = {
|
||||
width:img.width,
|
||||
height:img.height,
|
||||
}
|
||||
segmentImage(detailData.selectDetail.maskUrl,partialDesign,size).then(async (rv)=>{
|
||||
let front = detailData.frontBack.front[detailData.imgDomIndex]
|
||||
let back = detailData.frontBack.back[detailData.imgDomIndex]
|
||||
if(!front?.oldImageUrl)front.oldImageUrl = front.imageUrl
|
||||
if(!front?.oldMaskUrl)front.oldMaskUrl = front.maskUrl
|
||||
if(!back?.oldImageUrl)back.oldImageUrl = back.imageUrl
|
||||
if(!front?.oldMaskUrl)store.commit('DesignDetail/updataDetailItem',{maskUrl:front.oldMaskUrl})
|
||||
|
||||
front.imageUrl = rv.targetFrontUrl
|
||||
back.imageUrl = rv.targetBackUrl
|
||||
store.commit('DesignDetail/canvasPreviewUpdata',{type:detailData.isEditPattern.value?'all':detailData.currentDetailType,callBack:setRevocation})
|
||||
})
|
||||
}
|
||||
img.src = detailData.selectDetail.path
|
||||
saveCanvasJSONToSession()
|
||||
}
|
||||
}
|
||||
const saveCanvasJSONToSession = ()=>{
|
||||
let canvasJSON = detailDom.canvasBox.getCanvasJSON()
|
||||
const sessionCanvasList = sessionStorage.getItem('canvasList');
|
||||
const list = sessionCanvasList ? JSON.parse(sessionCanvasList) : []
|
||||
let index = -1
|
||||
list.forEach((item:any,i:number)=>{
|
||||
if(item.id == detailData.selectDetail.id){
|
||||
index = i
|
||||
}
|
||||
})
|
||||
if(index < 0){
|
||||
list.push({id:detailData.selectDetail.id,canvasJSON:canvasJSON})
|
||||
}else{
|
||||
list[index].canvasJSON = canvasJSON
|
||||
}
|
||||
sessionStorage.setItem('canvasList', JSON.stringify(list));
|
||||
}
|
||||
const detailEdit = async (str:any)=>{
|
||||
if(str){
|
||||
if(detailData.isEditPattern.value && detailData.isEditPattern.value == str){
|
||||
await detailDom.canvasBox.saveCanvas()
|
||||
// await detailDom.canvasBox.saveCanvas()
|
||||
await (detailDom.canvasBox as any).privewDetail()
|
||||
if(detailData.isEditPattern.value == 'canvasEditor')await uploadElement()
|
||||
detailData.isEditPattern.value = ''
|
||||
}else{
|
||||
if(detailData.isEditPattern.value){
|
||||
detailDom.canvasBox.editFront(str)
|
||||
}
|
||||
// if(detailData.isEditPattern.value && (str == 'canvasEditor' || str == 'redGreenExample')){
|
||||
// detailDom.canvasBox.editFront(str)
|
||||
// }
|
||||
detailDom.canvasBox.editFront(str)
|
||||
let otherData = await updateOtherLayers('single')
|
||||
await detailDom.canvasBox.updateOtherLayers(otherData)
|
||||
detailData.isEditPattern.value = str
|
||||
}
|
||||
}else{
|
||||
detailData.isEditPattern.value = ''
|
||||
}
|
||||
}
|
||||
const getColorName = (color:any)=>{
|
||||
let rgb:any = [color.r, color.g, color.b];
|
||||
let hsv = rgbToHsv(rgb);
|
||||
let data = [{
|
||||
h: hsv[0],
|
||||
s: hsv[1],
|
||||
v: hsv[2],
|
||||
}]
|
||||
return new Promise((resolve, reject) => {
|
||||
Https.axiosPost(Https.httpUrls.getRgbByHsvBatch, data)
|
||||
.then((rv) => {
|
||||
if (rv) {
|
||||
resolve(rv[0])
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
resolve({name:'--',tcx:'--'})
|
||||
});
|
||||
})
|
||||
}
|
||||
const updateOtherLayers = async (str:any='all')=>{//更新到画布图层
|
||||
let otherData:any = {}
|
||||
if(detailDom.detailRight?.privewDetail)await (detailDom.detailRight as any).privewDetail()
|
||||
if(str == 'all'){
|
||||
otherData = {
|
||||
color: detailData.selectDetail.newDetail?.color.r?detailData.selectDetail.newDetail?.color:detailData.selectDetail.color,
|
||||
printObject: detailData.selectDetail.newDetail?.print?.length>0?{prints:detailData.selectDetail.newDetail?.print}:detailData.selectDetail.printObject || null,
|
||||
trims: detailData.selectDetail.newDetail?.element?.length>0?detailData.selectDetail.newDetail?.element:detailData.selectDetail.trims || null,
|
||||
}
|
||||
}else if(str == 'single'){
|
||||
otherData = {
|
||||
color: detailData.selectDetail.color,
|
||||
printObject: detailData.selectDetail.printObject || null,
|
||||
trims: detailData.selectDetail.trims || null,
|
||||
}
|
||||
if(detailData.currentDetailType == 'color'){
|
||||
let color = detailData.selectDetail.newDetail?.color
|
||||
// let colorData:any = await getColorName(color?.rgba)
|
||||
if(detailData.selectDetail.newDetail?.color){
|
||||
if(color.r){
|
||||
color.rgba = {r:color.r,g:color.g,b:color.b,a:color.a}
|
||||
}else{
|
||||
color.rbga = {}
|
||||
}
|
||||
otherData.color = color
|
||||
}
|
||||
}
|
||||
if(detailData.currentDetailType == 'print'){
|
||||
otherData.printObject = {prints:detailData.selectDetail.newDetail?.print}
|
||||
}
|
||||
if(detailData.currentDetailType == 'element'){
|
||||
otherData.trims = detailData.selectDetail.newDetail?.element
|
||||
}
|
||||
}
|
||||
console.log(JSON.parse(JSON.stringify(otherData)),'=======',JSON.parse(JSON.stringify(detailData.selectDetail)))
|
||||
return otherData
|
||||
}
|
||||
const uploadElement = async ()=>{//取出画布数据更新到detail
|
||||
if(detailData.isEditPattern.value == 'canvasEditor'){
|
||||
// await detailDom.canvasBox.saveCanvas()
|
||||
const allInfo = await (detailDom.canvasBox as any).getCanvasElement()
|
||||
console.log(allInfo,'allInfo')
|
||||
if(allInfo.trims?.length > 0){
|
||||
// detailData.selectDetail.trims.prints = allInfo.trims
|
||||
let value = {
|
||||
data:allInfo.trims,
|
||||
str:'element'
|
||||
}
|
||||
store.commit('DesignDetail/setNewDetail',value)
|
||||
}
|
||||
if(allInfo.prints?.length > 0){
|
||||
// detailData.selectDetail.printObject.prints = allInfo.prints
|
||||
let value = {
|
||||
data:allInfo.prints,
|
||||
str:'print'
|
||||
}
|
||||
store.commit('DesignDetail/setNewDetail',value)
|
||||
}
|
||||
if(allInfo.color?.color?.rgba){
|
||||
let canvasColor = allInfo.color.color
|
||||
let colorData:any = await getColorName(allInfo.color.color?.rgba)
|
||||
let value:any = {
|
||||
data:{
|
||||
hsv:{
|
||||
h:colorData.h,
|
||||
s:colorData.s,
|
||||
v:colorData.v,
|
||||
},
|
||||
name:colorData.name,
|
||||
tcx:colorData.tcx,
|
||||
rgba:canvasColor.rgba,
|
||||
hex:rgbaToHex([canvasColor.rgba.r,canvasColor.rgba.g,canvasColor.rgba.b]),
|
||||
},
|
||||
str:'color'
|
||||
}
|
||||
if(canvasColor.gradient){
|
||||
value.data.gradient = canvasColor.gradient
|
||||
}
|
||||
store.commit('DesignDetail/setNewDetail',value)
|
||||
if(allInfo.color.color.gradient)detailData.selectDetail.color.gradient = allInfo.color.color.gradient
|
||||
if(detailData.currentDetailType == 'color'){
|
||||
detailData.detailLeftColorKey++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const canvasReload = async ()=>{
|
||||
if(detailData.isEditPattern.value){
|
||||
await detailDom.canvasBox.saveCanvas()
|
||||
}
|
||||
detailData.canvasKey += 1
|
||||
}
|
||||
let time = null as any
|
||||
const handleResize = ()=>{
|
||||
clearTimeout(time)
|
||||
time = setTimeout(()=>{
|
||||
store.commit('DesignDetail/setDesignDetail',detailData.designDetail)
|
||||
},1000)
|
||||
}
|
||||
const sketchSysToLibrary = ()=>{//系统sketch添加到library更新library
|
||||
coverRevocation()
|
||||
detailDom.detailLeft.sketchSysToLibrary()
|
||||
@@ -516,11 +710,6 @@ export default defineComponent({
|
||||
sessionStorage.setItem('revocation', JSON.stringify(revocation));
|
||||
sessionStorage.setItem('oppositeRevocation',JSON.stringify([]));
|
||||
}
|
||||
|
||||
const addDetail = () =>{
|
||||
let addDetails:any = detailDom.addDetails
|
||||
addDetails.init(detailData.selectDetail,'')
|
||||
}
|
||||
const setSloganData = (data:any)=>{
|
||||
detailData.selectDetail.sketchString = data
|
||||
if(detailData.currentDetailType == 'sketch' && detailData.selectDetail?.newDetail?.sketch){
|
||||
@@ -528,14 +717,12 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
onMounted(()=>{
|
||||
window.addEventListener('resize', handleResize);
|
||||
})
|
||||
onBeforeUnmount(()=>{
|
||||
sessionStorage.removeItem('oppositeRevocation')
|
||||
sessionStorage.removeItem('revocation')
|
||||
store.commit('DesignDetail/clearDesignDetail')
|
||||
|
||||
window.removeEventListener('resize', handleResize);
|
||||
})
|
||||
|
||||
return{
|
||||
@@ -551,10 +738,9 @@ export default defineComponent({
|
||||
oppositeRevocation,
|
||||
detailEdit,
|
||||
canvasReload,
|
||||
modelOnLoad,
|
||||
sketchSysToLibrary,
|
||||
addDetail,
|
||||
setSloganData,
|
||||
updateOtherLayers,//更新到画布图层 再canvasInit中执行
|
||||
}
|
||||
},
|
||||
|
||||
@@ -680,6 +866,24 @@ export default defineComponent({
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
&.canvas{
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
flex: auto;
|
||||
width: 35vw;
|
||||
height: 80vh;
|
||||
pointer-events: none;
|
||||
transform: translate(100vw,100vh);
|
||||
}
|
||||
&.active{
|
||||
position: relative;
|
||||
opacity: 1;
|
||||
flex: 1;
|
||||
width: auto;
|
||||
height: auto;
|
||||
pointer-events: auto;
|
||||
transform: translate(0,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
.btn{
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
<template>
|
||||
<div class="addDetailsModal generalModel" ref="addDetailsModal"></div>
|
||||
<a-modal
|
||||
class="addDetails_modal generalModel fullScreen"
|
||||
v-model:visible="addDetails"
|
||||
:footer="null"
|
||||
:get-container="() => $refs.addDetailsModal"
|
||||
width="100%"
|
||||
height="100%"
|
||||
:maskClosable="false"
|
||||
:centered="true"
|
||||
:closable="false"
|
||||
:destroyOnClose="true"
|
||||
wrapClassName="#app"
|
||||
:keyboard="false"
|
||||
:mask="false"
|
||||
>
|
||||
<div class="generalModel_btn">
|
||||
<div class="generalModel_closeIcon" @click.stop="cancelDsign()">
|
||||
<svg width="100%" height="100%" viewBox="0 0 46 46" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="23" cy="23" r="23" fill="white" fill-opacity="0.3"/>
|
||||
<rect x="32.5063" y="12" width="3" height="29" rx="1.5" transform="rotate(45 32.5063 12)" fill="#000"/>
|
||||
<rect x="34.6274" y="32.5059" width="3" height="29" rx="1.5" transform="rotate(135 34.6274 32.5059)" fill="#000"/>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="addDetails_center">
|
||||
<generalMiniCanvas :imgUrl="imgUrl" @submitBase64Data="submitBase64Data"></generalMiniCanvas>
|
||||
</div>
|
||||
<div></div>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent, ref, reactive, watch, onMounted, nextTick, toRefs } from "vue";
|
||||
import generalMiniCanvas from "../modules/generalMiniCanvas.vue";
|
||||
export default defineComponent({
|
||||
components: {
|
||||
generalMiniCanvas,
|
||||
},
|
||||
emits: ['setSloganData'],
|
||||
setup(props,{emit}) {
|
||||
let addDetail = reactive({
|
||||
imgUrl:''
|
||||
});
|
||||
let addDetails = ref(false);
|
||||
let init = (data,index)=>{
|
||||
addDetails.value = true
|
||||
addDetail.imgUrl = data.sketchString || data.path
|
||||
}
|
||||
let submitBase64Data = (data)=>{
|
||||
emit('setSloganData',data)
|
||||
cancelDsign()
|
||||
}
|
||||
let cancelDsign = ()=>{
|
||||
addDetails.value = false
|
||||
}
|
||||
return {
|
||||
...toRefs(addDetail),
|
||||
addDetails,
|
||||
init,
|
||||
submitBase64Data,
|
||||
cancelDsign,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.addDetailsModal{
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.addDetails_modal {
|
||||
.closeIcon {
|
||||
z-index: 2;
|
||||
}
|
||||
.addDetails_center{
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.exportCanvasBox_submit{
|
||||
// margin-top: 2.4rem;
|
||||
// text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -5,17 +5,16 @@
|
||||
<div class="canvasContent" ref="canvasContent">
|
||||
<div class="content-bottom" ref="canvasContent">
|
||||
<div class="contet">
|
||||
<!-- :clothingImageUrl="selectDetail?.undividedLayerWithSinglePrint || selectDetail.undividedLayer || selectDetail.path" -->
|
||||
<div class="canvas" v-if="currentView === 'canvasEditor'" @click.stop>
|
||||
<div class="canvas" :class="{'active': currentView === 'canvasEditor'}"@click.stop>
|
||||
<editCanvas v-if="canvasLoad" :config="canvasConfig"
|
||||
@canvasInit="canvasInit"
|
||||
@changeCanvas="changeCanvas"
|
||||
@canvasInit="editSketchCanvasInit"
|
||||
is-edit
|
||||
:clothingMinIOPath="selectDetail.minIOPath"
|
||||
:clothingImageUrl="selectDetail.path"
|
||||
:clothingImageUrl2="selectDetail.undividedLayer"
|
||||
:clothingImageUrl2="selectDetail.layersObject[0].maskUrl"
|
||||
showFixedLayer
|
||||
:canvasJSON="canvasJSON"
|
||||
:otherData="otherData"
|
||||
@canvasLoadJsonSuccess="canvasLoadJsonSuccess"
|
||||
:clothing-image-opts="{
|
||||
imageMode:'contains',
|
||||
}"
|
||||
@@ -25,14 +24,8 @@
|
||||
<!-- <canvasContent ref="canvasContent"></canvasContent> -->
|
||||
</div>
|
||||
<div class="editFrontBack" v-if="currentView === 'redGreenExample'" @click.stop>
|
||||
<!-- <editFrontBack
|
||||
:patchData="frontBack"
|
||||
:imgDomIndex="imgDomIndex"
|
||||
|
||||
ref="editFrontBack">
|
||||
</editFrontBack> -->
|
||||
<editCanvas v-if="canvasLoad" :config="canvasConfig"
|
||||
@canvasInit="canvasInit"
|
||||
@canvasInit="editFrontBackCanvasInit"
|
||||
:enabledRedGreenMode="true"
|
||||
:clothingImageUrl="selectDetail.path"
|
||||
:redGreenImageUrl="frontBack.front[imgDomIndex].maskUrl"
|
||||
@@ -45,6 +38,9 @@
|
||||
ref="editCanvasBackFront">
|
||||
</editCanvas>
|
||||
</div>
|
||||
<div class="editSketch" v-if="currentView === 'editSketch'" @click.stop>
|
||||
<generalMiniCanvas ref="generalMiniCanvas" :btnShow="false" :imgUrl="selectDetail.sketchString || selectDetail.path"></generalMiniCanvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="Finish">
|
||||
@@ -69,16 +65,25 @@ import { useI18n } from 'vue-i18n'
|
||||
import editCanvas from "@/component/Canvas/CanvasEditor/index.vue";
|
||||
import { formatTime,segmentImage,getMinioUrl } from "@/tool/util";
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import generalMiniCanvas from "../../modules/generalMiniCanvas.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components:{
|
||||
editCanvas
|
||||
editCanvas,generalMiniCanvas
|
||||
},
|
||||
props:{
|
||||
isEditPattern:{
|
||||
type:String,
|
||||
default:''
|
||||
}
|
||||
},
|
||||
updateOtherLayers:{
|
||||
type:Function,
|
||||
default:()=>{}
|
||||
},
|
||||
sketchSize:{
|
||||
type:Object,
|
||||
default:()=>{}
|
||||
},
|
||||
},
|
||||
setup(props,{emit}) {
|
||||
const store = useStore();
|
||||
@@ -90,6 +95,7 @@ export default defineComponent({
|
||||
editCanvas:null as any,
|
||||
editCanvasBackFront:null as any,
|
||||
canvasContent:null as any,
|
||||
generalMiniCanvas:null as any,
|
||||
})
|
||||
const userDetail = computed(()=>{
|
||||
return store.state.UserHabit.userDetail
|
||||
@@ -130,14 +136,14 @@ export default defineComponent({
|
||||
if(detailData.currentView === 'canvasEditor'){
|
||||
sessionStorage.setItem('sketchEdit',detailDom.editCanvas.getJSON())
|
||||
canvasJSON = sessionStorage.getItem('frontBackEdit');
|
||||
}else{
|
||||
}else if(detailData.currentView === 'redGreenExample'){
|
||||
sessionStorage.setItem('frontBackEdit',detailDom.editCanvasBackFront.getJSON())
|
||||
canvasJSON = sessionStorage.getItem('sketchEdit');
|
||||
}
|
||||
detailData.canvasLoad = false
|
||||
// detailData.canvasLoad = false
|
||||
detailData.currentView = str
|
||||
if(canvasJSON){
|
||||
detailData.canvasLoad = true
|
||||
// detailData.canvasLoad = true
|
||||
nextTick(()=>{
|
||||
if(detailData.currentView === 'redGreenExample'){
|
||||
detailDom.editCanvas.loadJSON(canvasJSON)
|
||||
@@ -148,29 +154,51 @@ export default defineComponent({
|
||||
}else{
|
||||
if(detailData.currentView === 'redGreenExample'){
|
||||
nextTick(()=>{
|
||||
setCanvas(detailData.selectDetail?.undividedLayerWithSinglePrint || detailData.selectDetail.undividedLayer || detailData.selectDetail.path).then(()=>{
|
||||
detailData.canvasLoad = true
|
||||
setCanvas(detailData.selectDetail.path).then(()=>{
|
||||
// detailData.canvasLoad = true
|
||||
})
|
||||
})
|
||||
}else{
|
||||
nextTick(()=>{
|
||||
setCanvas(detailData.frontBack.front[detailData.imgDomIndex].maskUrl).then(()=>{
|
||||
detailData.canvasLoad = true
|
||||
// detailData.canvasLoad = true
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
const privewDetail = async (oldSelectDetail = detailData.selectDetail)=>{
|
||||
const updateOtherLayers = (obj:any)=>{
|
||||
if(!detailDom.editCanvas)return
|
||||
return new Promise((res,reject)=>{
|
||||
detailDom.editCanvas.exportImage({isContainBg:false,isContainFixed:false}).then((rv)=>{
|
||||
if(oldSelectDetail?.partialDesign)oldSelectDetail.partialDesign.partialDesignBase64 = rv
|
||||
res('')
|
||||
})
|
||||
return new Promise(async (res,reject)=>{
|
||||
await detailDom?.editCanvas.updateOtherLayers(obj)
|
||||
res('')
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
const privewDetail = async (oldSelectDetail = detailData.selectDetail)=>{
|
||||
// if(!detailDom.editCanvas)return
|
||||
return new Promise(async (res,reject)=>{
|
||||
await detailDom.editCanvas.exportImage({
|
||||
isContainFixed:true,
|
||||
width:props.sketchSize.width,
|
||||
height:props.sketchSize.height,
|
||||
}).then((rv)=>{
|
||||
if(oldSelectDetail?.partialDesign)oldSelectDetail.partialDesign.partialDesignBase64 = rv
|
||||
})
|
||||
//包含平铺图层 single+overall模式的图
|
||||
// await detailDom.editCanvas.exportImage({isContainFixed:true,isPrintTrimsNoRepeat:true,isPrintTrimsRepeat:true,isContainNormalLayer:false}).then((rv)=>{
|
||||
// oldSelectDetail.undividedLayerWithSinglePrint = rv
|
||||
// })
|
||||
//不包含平铺图层overall模式的图
|
||||
// await detailDom.editCanvas.exportImage({isContainFixed:true,isPrintTrimsNoRepeat:false,isPrintTrimsRepeat:true,isContainNormalLayer:false}).then((rv)=>{
|
||||
// oldSelectDetail.undividedLayer = rv
|
||||
// })
|
||||
await setUndivideLayer()
|
||||
res('')
|
||||
})
|
||||
}
|
||||
const getCanvasElement = ()=>{
|
||||
if(!detailDom?.editCanvas)return ''
|
||||
return detailDom?.editCanvas.exportExtraInfo();
|
||||
}
|
||||
const setFrontBackColor = (data:any)=>{
|
||||
detailDom.editFrontBack.setBackground(data)
|
||||
@@ -180,11 +208,14 @@ export default defineComponent({
|
||||
let img = new Image()
|
||||
img.onload = ()=>{
|
||||
let wH = [1,1]
|
||||
let domHeight = detailDom.canvasContent.offsetHeight - 200
|
||||
let domHeight = detailDom.canvasContent?.offsetHeight - 200
|
||||
let imgHeight = img.height
|
||||
wH = [1,domHeight/imgHeight]
|
||||
detailData.canvasConfig.width = img.width * wH[1]
|
||||
detailData.canvasConfig.height = domHeight
|
||||
// detailData.canvasConfig.width = img.width * wH[1]
|
||||
// detailData.canvasConfig.height = domHeight
|
||||
console.log(detailData.canvasConfig,'===============')
|
||||
detailData.canvasConfig.width = img.width
|
||||
detailData.canvasConfig.height = img.height
|
||||
|
||||
res('')
|
||||
}
|
||||
@@ -234,7 +265,7 @@ export default defineComponent({
|
||||
|
||||
|
||||
const frontBackChange = (value:any)=>{
|
||||
let full = detailData.frontBack.front[detailData.imgDomIndex]?.undividedLayerWithSinglePrint || detailData.frontBack.front[detailData.imgDomIndex].undividedLayer || detailData.selectDetail.path
|
||||
let full = detailData.selectDetail.partialDesign.partialDesignBase64 || detailData.selectDetail.path
|
||||
let size = {
|
||||
...detailData.canvasConfig,
|
||||
}
|
||||
@@ -255,28 +286,34 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
}
|
||||
const canvasInit = (value:any)=>{
|
||||
const editSketchCanvasInit = (value:any)=>{
|
||||
detailData.canvasInstance = value
|
||||
detailData.getCanvasIfEdit.fun = getCanvasLength
|
||||
detailData.isShowMark = false
|
||||
console.log('初始化完成')
|
||||
}
|
||||
const editFrontBackCanvasInit = (value:any)=>{
|
||||
detailData.canvasInstance = value
|
||||
detailData.getCanvasIfEdit.fun = getCanvasLength
|
||||
detailData.isShowMark = false
|
||||
console.log('初始化完成')
|
||||
}
|
||||
const getCanvasLength = ()=>{
|
||||
return detailData.canvasInstance?.commandManager?.undoStack?.length
|
||||
}
|
||||
const saveCanvas = async (type:string = '')=>{
|
||||
if(type !== 'auto'){
|
||||
detailData.isShowMark = true
|
||||
}
|
||||
const index = detailData.designDetail.clothes.findIndex(item => item.id === detailData.selectDetail.id);
|
||||
const getCanvasJSON = ()=>{
|
||||
if(!detailDom?.editCanvas)return ''
|
||||
return detailDom?.editCanvas?.getJSON()
|
||||
}
|
||||
const saveCanvas = async (canvasData:any)=>{
|
||||
const index = detailData.designDetail.clothes.findIndex(item => item.id === canvasData.id);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
if(!detailDom?.editCanvas)return resolve()
|
||||
let canvasJSON = detailDom?.editCanvas?.getJSON()
|
||||
let canvasData = JSON.parse(canvasJSON)
|
||||
if(!canvasData)return resolve()
|
||||
canvasData.canvas.objects.forEach((objectsItem:any) => {
|
||||
if(objectsItem.type == 'image')objectsItem.minioUrl = getMinioUrl(objectsItem.src)
|
||||
});
|
||||
let blob = new Blob([JSON.stringify(canvasData)], { type: "application/json" });
|
||||
let canvasJSON = JSON.parse(canvasData.canvasJSON)
|
||||
if(!canvasJSON)return resolve()
|
||||
// canvasData.canvas.objects.forEach((objectsItem:any) => {
|
||||
// if(objectsItem.type == 'image')objectsItem.minioUrl = getMinioUrl(objectsItem.src)
|
||||
// });
|
||||
let blob = new Blob([JSON.stringify(canvasJSON)], { type: "application/json" });
|
||||
let formData = new FormData();
|
||||
formData.append("file", blob, "data.json");
|
||||
formData.append("designItemDetailId", detailData.selectDetail.id);
|
||||
@@ -297,12 +334,52 @@ export default defineComponent({
|
||||
});
|
||||
})
|
||||
}
|
||||
let time = null as any
|
||||
const changeCanvas = ()=>{
|
||||
clearTimeout(time)
|
||||
time = setTimeout(()=>{
|
||||
saveCanvas('auto')
|
||||
},3000)
|
||||
// let time = null as any
|
||||
// const changeCanvas = ()=>{
|
||||
// clearTimeout(time)
|
||||
// time = setTimeout(()=>{
|
||||
// saveCanvas('auto')
|
||||
// },3000)
|
||||
// }
|
||||
const canvasLoadJsonSuccess = async ()=>{
|
||||
let otherData = await props.updateOtherLayers()
|
||||
|
||||
await updateOtherLayers(otherData)
|
||||
await setUndivideLayer()
|
||||
}
|
||||
const setUndivideLayer = async ()=>{
|
||||
await new Promise<void>(async (resolve, reject) => {
|
||||
// if(!detailData.selectDetail.undividedLayerColor){
|
||||
await detailDom.editCanvas.exportImage({
|
||||
isContainFixed:true,
|
||||
isPrintTrimsRepeat:false,
|
||||
isPrintTrimsNoRepeat:false,
|
||||
isContainNormalLayer:false,
|
||||
width:props.sketchSize.width,
|
||||
height:props.sketchSize.height}).then((rv)=>{
|
||||
detailData.selectDetail.undividedLayerColor = rv
|
||||
})
|
||||
// }
|
||||
// if(!detailData.selectDetail.undividedLayer){
|
||||
await detailDom.editCanvas.exportImage({
|
||||
isContainFixed:true,
|
||||
isPrintTrimsNoRepeat:false,
|
||||
isPrintTrimsRepeat:true,
|
||||
isContainNormalLayer:false,
|
||||
width:props.sketchSize.width,
|
||||
height:props.sketchSize.height,
|
||||
}).then((rv)=>{
|
||||
console.log(rv,'======')
|
||||
detailData.selectDetail.undividedLayer = rv
|
||||
})
|
||||
// }
|
||||
resolve()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
const submitBase64Data = ()=>{
|
||||
return detailDom.generalMiniCanvas.submitBase64Data()
|
||||
}
|
||||
|
||||
onBeforeUnmount(()=>{
|
||||
@@ -322,6 +399,12 @@ export default defineComponent({
|
||||
onMounted(()=>{
|
||||
nextTick(async ()=>{
|
||||
// detailData.currentView = 'canvasEditor'
|
||||
const sessionCanvasList = sessionStorage.getItem('canvasList');
|
||||
const canvasList = sessionCanvasList ? JSON.parse(sessionCanvasList) : []
|
||||
let canvasIndex = canvasList.findIndex(item => item.id === detailData.selectDetail.id);
|
||||
if(canvasIndex>1){
|
||||
detailData.canvasJSON = canvasList[canvasIndex].canvasJSON
|
||||
}
|
||||
if(detailData.selectDetail.canvasId){
|
||||
detailData.isShowMark = true
|
||||
await new Promise((resolve, reject) => {
|
||||
@@ -339,7 +422,7 @@ export default defineComponent({
|
||||
});
|
||||
})
|
||||
}
|
||||
setCanvas(detailData.selectDetail?.undividedLayerWithSinglePrint || detailData.selectDetail.undividedLayer || detailData.selectDetail.path).then(()=>{
|
||||
setCanvas(detailData.selectDetail.path).then(()=>{
|
||||
detailData.canvasLoad = true
|
||||
})
|
||||
})
|
||||
@@ -351,9 +434,14 @@ export default defineComponent({
|
||||
privewDetail,
|
||||
setFrontBackColor,
|
||||
frontBackChange,
|
||||
canvasInit,
|
||||
editSketchCanvasInit,
|
||||
editFrontBackCanvasInit,
|
||||
saveCanvas,
|
||||
changeCanvas,
|
||||
getCanvasElement,
|
||||
updateOtherLayers,
|
||||
canvasLoadJsonSuccess,
|
||||
submitBase64Data,
|
||||
getCanvasJSON,
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
@@ -434,7 +522,15 @@ export default defineComponent({
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
.canvas,.editFrontBack{
|
||||
.canvas{
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
&.active{
|
||||
opacity: 1;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
.canvas,.editFrontBack,.editSketch{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ import SelectImages from '@/component/common/SelectImages.vue'
|
||||
|
||||
import upload from './upload.vue'
|
||||
import pallet from './pallet.vue'
|
||||
import { select } from 'three/tsl';
|
||||
export default defineComponent({
|
||||
components:{
|
||||
upload,pallet,SelectImages
|
||||
@@ -104,8 +105,12 @@ export default defineComponent({
|
||||
newVal.name = data.name
|
||||
newVal.tcx = data.tcx
|
||||
colorData.colorList.list[colorData.selectDetail.id][colorData.colorList.index] = newVal
|
||||
data.rgba = newVal.rgba
|
||||
if(newVal.gradient){
|
||||
data.gradient = newVal.gradient
|
||||
}
|
||||
let value = {
|
||||
data:newVal,
|
||||
data:data,
|
||||
str:'color'
|
||||
}
|
||||
store.commit('DesignDetail/setNewDetail',value)
|
||||
@@ -132,16 +137,14 @@ export default defineComponent({
|
||||
item = colorData.allBoardData.colorBoards[index]
|
||||
let color = colorData.allBoardData.colorBoards?.[index]
|
||||
if(!color?.rgba && color?.rgbValue)color.rgba = color.rgbValue
|
||||
|
||||
if(
|
||||
colorData.allBoardData.colorBoards?.[index] &&
|
||||
colorData.selectDetail.color.rgba?.r == color?.rgba?.r &&
|
||||
colorData.selectDetail.color.rgba?.g == color?.rgba?.g &&
|
||||
colorData.selectDetail.color.rgba?.b == color?.rgba?.b &&
|
||||
JSON.stringify(colorData.selectDetail.color.gradient) == JSON.stringify(color?.gradient)
|
||||
colorData.selectDetail.color.rgba?.b == color?.rgba?.b ||
|
||||
(JSON.stringify(colorData.selectDetail.color.gradient) == JSON.stringify(color?.gradient) && JSON.stringify(colorData.selectDetail.color.gradient))
|
||||
&& colorData.selectDetail.color.rgba?.r
|
||||
){
|
||||
console.log('---',index)
|
||||
isNoSelect = true
|
||||
colorData.selectColor = item
|
||||
colorData.colorList.index = index
|
||||
@@ -190,52 +193,11 @@ export default defineComponent({
|
||||
colorData.colorList.index = num
|
||||
colorData.colorList.list[newVal][num] = item
|
||||
}
|
||||
|
||||
// for (let index = 0; index < 9; index++) {
|
||||
// colorData.allBoardData.colorBoards
|
||||
// let item:any = {}
|
||||
// item = colorData.allBoardData.colorBoards[index]
|
||||
// if(
|
||||
// colorData.allBoardData.colorBoards?.[index] &&
|
||||
// colorData.selectDetail.color.rgba?.r == colorData.allBoardData.colorBoards?.[index]?.rgba?.r &&
|
||||
// colorData.selectDetail.color.rgba?.g == colorData.allBoardData.colorBoards?.[index]?.rgba?.g &&
|
||||
// colorData.selectDetail.color.rgba?.b == colorData.allBoardData.colorBoards?.[index]?.rgba?.b &&
|
||||
// JSON.stringify(colorData.selectDetail.color.gradient) == JSON.stringify(colorData.allBoardData.colorBoards?.[index]?.gradient)
|
||||
// && colorData.selectDetail.color.rgba?.r
|
||||
// ){
|
||||
// console.log(13212)
|
||||
// if(!isOneChecked){
|
||||
// isOneChecked = true
|
||||
// if(colorData.allBoardData.colorBoards?.[index].gradient){
|
||||
// item.gradient = colorData.allBoardData.colorBoards?.[index].gradient
|
||||
// }
|
||||
// colorData.selectColor = item
|
||||
// colorData.colorList.index = index
|
||||
// }
|
||||
// }else if(colorData.selectDetail.color.rgba?.r){
|
||||
// if(!isNoSelect){
|
||||
// item = {
|
||||
// hex:rgbaToHex([colorData.selectDetail.color.rgba.r,colorData.selectDetail.color.rgba.g,colorData.selectDetail.color.rgba.b]),
|
||||
// id:colorData.selectDetail.color.id,
|
||||
// rgba:{
|
||||
// r:colorData.selectDetail.color.rgba.r,
|
||||
// g:colorData.selectDetail.color.rgba.g,
|
||||
// b:colorData.selectDetail.color.rgba.b,
|
||||
// },
|
||||
// tcx:colorData.selectDetail.color.tcx,
|
||||
// name:colorData.selectDetail.color.name,
|
||||
// }
|
||||
// if(colorData.selectDetail.color.gradient){
|
||||
// item.gradient = colorData.selectDetail.color.gradient
|
||||
// }
|
||||
// colorData.colorList.index = index
|
||||
// colorData.selectColor = item
|
||||
// isNoSelect = true
|
||||
// }else{
|
||||
// item = {}
|
||||
// }
|
||||
// }
|
||||
// colorData.colorList.list[newVal].push(item)
|
||||
// let newData = colorData.selectDetail?.newDetail?.color
|
||||
// console.log(newData,colorData.selectColor)
|
||||
// if(newData){
|
||||
// newData.hex = rgbaToHex([newData.rgba.r,newData.rgba.g,newData.rgba.b])
|
||||
// colorData.selectColor = newData
|
||||
// }
|
||||
},{immediate: true})
|
||||
const selectImgItem = ()=>{
|
||||
@@ -281,7 +243,6 @@ export default defineComponent({
|
||||
})
|
||||
}
|
||||
const handleShowListChange=(val:boolean)=>{
|
||||
console.log('val',val)
|
||||
showLibrary.value = !val
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="detailLeft">
|
||||
<sketch v-show="currentDetailType == 'sketch'" ref="sketch" @addDetail="addDetail"></sketch>
|
||||
<print v-show="currentDetailType == 'print'"></print>
|
||||
<color v-if="currentDetailType == 'color'"></color>
|
||||
<color v-if="currentDetailType == 'color'" :key="detailLeftColorKey"></color>
|
||||
<element v-show="currentDetailType == 'element'"></element>
|
||||
<accessory v-show="currentDetailType == 'accessory'"></accessory>
|
||||
<models v-show="currentDetailType == 'models'"></models>
|
||||
@@ -26,6 +26,12 @@ export default defineComponent({
|
||||
components:{
|
||||
sketch,print,color,element,models,accessory
|
||||
},
|
||||
props:{
|
||||
detailLeftColorKey:{
|
||||
type:Number,
|
||||
default:0,
|
||||
},
|
||||
},
|
||||
emit:['addDetail'],
|
||||
setup(props,{emit}) {
|
||||
const store = useStore();
|
||||
|
||||
@@ -69,7 +69,6 @@ export default defineComponent({
|
||||
file.designType = file?.resData?.designType
|
||||
}
|
||||
}
|
||||
// store.commit('DesignDetail/setNewDetail',file.resData)
|
||||
emit('selectImgItem',file)
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,6 @@ export default defineComponent({
|
||||
})
|
||||
const selectImgItem = (file:any)=>{
|
||||
// let data = JSON.parse(JSON.stringify(file))
|
||||
// store.commit('DesignDetail/setNewDetail',file)
|
||||
emit('selectImgItem',file)
|
||||
}
|
||||
const upFileUploadChange = (data:any)=>{
|
||||
|
||||
@@ -88,7 +88,6 @@ export default defineComponent({
|
||||
})
|
||||
const selectImgItem = (file:any)=>{
|
||||
// let data = JSON.parse(JSON.stringify(file))
|
||||
// store.commit('DesignDetail/setNewDetail',file)
|
||||
emit('selectImgItem',file)
|
||||
}
|
||||
const setUploadImgList = (list:any)=>{
|
||||
|
||||
@@ -16,20 +16,21 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="printElementListBorder" :class="{'single':stateOverallSingle == 'single'}">
|
||||
<div class="printElementList" ref="printElementList" :style="{height:stateOverallSingle == 'overall'?'14rem':'10rem'}">
|
||||
<div class="itemList" :style="{width:stateOverallSingle == 'single'?(prentWidth || 0) : '100%'}">
|
||||
<div class="printElementListBorder single">
|
||||
<div class="printElementList" ref="printElementList" :style="{height:'10rem'}">
|
||||
<div class="itemList" :style="{width:(prentWidth || 0)}">
|
||||
<div class="imgItem"
|
||||
v-if="stateOverallSingle == 'single'"
|
||||
v-for="item in printStyleList[type][stateOverallSingle]"
|
||||
v-for="item,index in printStyleList[type][stateOverallSingle]"
|
||||
:key="item"
|
||||
:class="{active:stateOverallSingle == 'overall' && index == imgDomIndex}"
|
||||
@mousedown.stop="designMousedown(getMousePosition($event,false),item.uniqueId,'disLike')"
|
||||
@touchstart.passive="designMousedown(getMousePosition($event,true),item.uniqueId,'disLike')"
|
||||
@click.stop="overallSetIndex(item)"
|
||||
>
|
||||
<img :src="item.path" alt="">
|
||||
<i class="fi fi-rr-trash" @click="navDelete(item)"></i>
|
||||
<i class="fi fi-rr-trash" @click.stop="navDelete(item)"></i>
|
||||
</div>
|
||||
<div class="imgItem"
|
||||
<!-- <div class="imgItem"
|
||||
v-if="stateOverallSingle == 'overall'"
|
||||
v-for="item in printStyleList[type][stateOverallSingle]"
|
||||
:key="item"
|
||||
@@ -37,31 +38,49 @@
|
||||
>
|
||||
<img :src="item.path" alt="">
|
||||
<i class="fi fi-rr-trash" @click="navDelete(item)"></i>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="stateOverallSingle != 'single'" class="habit_System_Designer">
|
||||
<div class="habit_System_Designer_text">{{ $t('DesignPrintOperation.Scale') }}</div>
|
||||
<a-slider id="system_silder"
|
||||
<div v-if="imgDomIndex >= 0" v-show="stateOverallSingle != 'single'" class="habit_System_Designer">
|
||||
<!-- <a-slider id="system_silder"
|
||||
class="system_silder"
|
||||
:min="20"
|
||||
:max="1000"
|
||||
v-model:value="systemDesignerPercentage"
|
||||
:tip-formatter="formatter"
|
||||
>
|
||||
</a-slider>
|
||||
</a-slider> -->
|
||||
<a-popover
|
||||
trigger="click"
|
||||
destroyTooltipOnHide
|
||||
:title="t('Canvas.repeatSetting')"
|
||||
>
|
||||
<template #content>
|
||||
<repeat-setting
|
||||
:object="printStyleList[type].overall[imgDomIndex]"
|
||||
@inputFillAngle="inputFillAngle"
|
||||
@inputFillOffset="inputFillOffset"
|
||||
@inputFillScale="inputFillScale"
|
||||
:sketchPath="selectDetail.path"
|
||||
@inputFill_Gap="
|
||||
(x, y) => inputFill_Gap(x, y)"
|
||||
/>
|
||||
</template>
|
||||
<div class="btn">
|
||||
<SvgIcon name="overallMore" size="20" />
|
||||
</div>
|
||||
</a-popover>
|
||||
</div>
|
||||
<div class="editPrintElementBox">
|
||||
<div class="designOpenrtion_centent" id="designOpenrtionCentent">
|
||||
<!-- <div class="designOpenrtion_imgMask" :style="sketch"> -->
|
||||
<div class="designOpenrtion_imgMask">
|
||||
<div class="designOpenrtion_print">
|
||||
<div class="designOpenrtion_print" v-if="stateOverallSingle == 'single'">
|
||||
<div
|
||||
v-for="item,index in printStyleList[type][stateOverallSingle]"
|
||||
:key="item"
|
||||
v-if="stateOverallSingle == 'single'"
|
||||
:style="item?.pattern?.style"
|
||||
:style="{...item?.pattern?.style,opacity:item?.object?.opacity || 1}"
|
||||
@mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))"
|
||||
class="modal_imgItem"
|
||||
ref="content"
|
||||
@@ -70,9 +89,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- <img :src="selectDetail.path" alt="" class="designOpenrtion_sketch" ref="sketchImg"> -->
|
||||
<img :src="selectDetail?.undividedLayer?selectDetail.undividedLayer:selectDetail.path" alt="" class="designOpenrtion_sketch" ref="sketchImg" @load="()=>isSketchLoad = true">
|
||||
<div class="designOpenrtion_btn">
|
||||
<ul v-if="stateOverallSingle == 'single'" v-for="item,index in printStyleList[type][stateOverallSingle]" :key="item" :class="{active:item?.pattern.designOpenrtionBtn?item?.pattern.designOpenrtionBtn:false}" class="designOpenrtion_Mousingle" :style="item?.pattern.style" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))">
|
||||
<img :src="stateOverallSingle == 'single'?(selectDetail.undividedLayer||selectDetail.path):(selectDetail.undividedLayerColor || selectDetail.path)" alt="" class="designOpenrtion_sketch" ref="sketchImg" @load="()=>isSketchLoad = true">
|
||||
<img :src="selectDetail.sketchMask" alt="" class="designOpenrtion_sketchMask" ref="sketchMask">
|
||||
<div class="designOpenrtion_btn" v-if="stateOverallSingle == 'single'" >
|
||||
<ul v-for="item,index in printStyleList[type][stateOverallSingle]" :key="item" :class="{active:item?.pattern.designOpenrtionBtn?item?.pattern.designOpenrtionBtn:false}" class="designOpenrtion_Mousingle" :style="item?.pattern.style" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))">
|
||||
<li class="designOpenrtion_btn_top" @mousedown.stop="itemSizeMousedown('top',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('top',getMousePosition($event,true))"></li>
|
||||
<li class="designOpenrtion_btn_bottom" @mousedown.stop="itemSizeMousedown('bottom',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('bottom',getMousePosition($event,true))"></li>
|
||||
<li class="designOpenrtion_btn_left" @mousedown.stop="itemSizeMousedown('left',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('left',getMousePosition($event,true))"></li>
|
||||
@@ -82,12 +102,15 @@
|
||||
<img src="../../../assets/images/homePage/cuowu.svg" alt="">
|
||||
</li>
|
||||
</ul>
|
||||
<div v-show="stateOverallSingle != 'single'"></div>
|
||||
<!-- <div v-show="stateOverallSingle != 'single'"></div>
|
||||
<ul v-if="stateOverallSingle != 'single' && printStyleList[type][stateOverallSingle][0]" class="designOpenrtion_Mouoverall active" :style="'left:'+printStyleList[type][stateOverallSingle][0]?.pattern?.style?.left+';top:'+printStyleList[type][stateOverallSingle][0]?.pattern?.style?.top+';'" @mousedown.stop="itemMoveMousedown(0,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(0,getMousePosition($event,true))">
|
||||
<i class="fi fi-rr-arrows animtion1"></i>
|
||||
<i class="fi fi-rr-arrows animtion2"></i>
|
||||
<li class="designOpenrtion_rotote" v-rotote.stop="[0,printStyleList[type][stateOverallSingle][0]?.pattern?.transform,type]"></li>
|
||||
</ul>
|
||||
</ul> -->
|
||||
</div>
|
||||
<div class="designOpenrtion_pingpu" v-else>
|
||||
<pingpu :list="printStyleList[type].overall" :width="sketchSize.width" :height="sketchSize.height" ref="pingpuRef" @change-canvas="updateCanvas"></pingpu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -106,20 +129,31 @@ import { defineComponent,computed,ref,onMounted,nextTick,watch,toRefs, reactive,
|
||||
// import setDesignItem from '@/component/Detail/setDesignItem2.vue'
|
||||
import { useStore } from "vuex";
|
||||
import { getMousePosition } from "@/tool/mdEvent";
|
||||
import { sketchToMask } from "@/tool/util";
|
||||
import pingpu from '@/component/Canvas/OverallCanvas/index.vue'
|
||||
import RepeatSetting from "./overallSetting/RepeatSetting.vue";
|
||||
import { useI18n } from 'vue-i18n'
|
||||
export default defineComponent({
|
||||
components:{
|
||||
pingpu,RepeatSetting
|
||||
},
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'print',
|
||||
}
|
||||
},
|
||||
sketchSize:{
|
||||
type:Object,
|
||||
default:()=>{}
|
||||
},
|
||||
},
|
||||
setup(props,{emit}) {
|
||||
const { t } = useI18n()
|
||||
const store = useStore();
|
||||
const editPrintElementDom = reactive({
|
||||
imgDom:null as any,
|
||||
sketchImg:null as any,
|
||||
pingpuRef:null as any,
|
||||
})
|
||||
const editPrintElementData = reactive({
|
||||
selectDetail:computed(()=>store.state.DesignDetail.selectDetail),
|
||||
@@ -135,9 +169,9 @@ export default defineComponent({
|
||||
single:[],
|
||||
}
|
||||
} as any,
|
||||
stateOverallSingle:'single',
|
||||
stateOverallSingle:'single' as any,
|
||||
type:props.type,
|
||||
imgDomIndex:0,
|
||||
imgDomIndex:-1,
|
||||
direction:'',//判断点的那条边
|
||||
printZIndex:2,//印花优先级
|
||||
sketchWH:{
|
||||
@@ -149,6 +183,13 @@ export default defineComponent({
|
||||
printElementList:null as any,
|
||||
isSketchLoad:false,
|
||||
})
|
||||
const ACTIONS = {
|
||||
ADD: "add",
|
||||
UPDATE: "update",
|
||||
DELETE: "delete",
|
||||
SELECT: "select",
|
||||
SORT: "sort",
|
||||
};
|
||||
const collItemSize = reactive({
|
||||
collValue:18,
|
||||
num:1,
|
||||
@@ -168,13 +209,14 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
const setOveralSingle = ()=>{
|
||||
|
||||
setItemPosition()
|
||||
}
|
||||
const formatter = (value:any)=>{
|
||||
return `${value}%`;
|
||||
}
|
||||
const deletePrint = ()=>{
|
||||
editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle].splice(editPrintElementData.imgDomIndex,1)
|
||||
if(editPrintElementData.imgDomIndex>-1)editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle].splice(editPrintElementData.imgDomIndex,1)
|
||||
}
|
||||
const setScaleLocation:any = (item:any)=>{
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -215,6 +257,7 @@ export default defineComponent({
|
||||
path:data.url,
|
||||
priority:editPrintElementData.printZIndex,
|
||||
scale,
|
||||
globalCompositeOperation:'',
|
||||
}
|
||||
getItemPosition(item)
|
||||
setItemPosition()
|
||||
@@ -249,7 +292,9 @@ export default defineComponent({
|
||||
path:item.path,
|
||||
minIOPath:item.minIOPath,
|
||||
ifSingle:!!item.ifSingle,
|
||||
globalCompositeOperation:'',
|
||||
}
|
||||
if(item.object)value.object = item.object
|
||||
return value
|
||||
}
|
||||
if(editPrintElementData.printStyleList[props.type].single.length>0){
|
||||
@@ -282,7 +327,6 @@ export default defineComponent({
|
||||
}
|
||||
const getItemPosition = (item:any)=>{
|
||||
let left,top
|
||||
|
||||
if(item.ifSingle){
|
||||
//single
|
||||
left = item.location[0] / editPrintElementData.sketchWH.scale[0]
|
||||
@@ -312,13 +356,40 @@ export default defineComponent({
|
||||
}
|
||||
editPrintElementData.printZIndex++
|
||||
item.pattern = pattern
|
||||
|
||||
if(item.object){
|
||||
|
||||
}else{
|
||||
item.object = {
|
||||
top: 0,
|
||||
left: 0,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
opacity: 1,
|
||||
angle: 0,
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
blendMode: "multiply",
|
||||
gapX: 0,
|
||||
gapY: 0,
|
||||
}
|
||||
}
|
||||
if(item.ifSingle){
|
||||
editPrintElementData.printStyleList[props.type].single.push(item)
|
||||
}else{
|
||||
editPrintElementData.printStyleList[props.type].overall = []
|
||||
item.token = Date.now().toString() + (editPrintElementData.printStyleList[props.type].overall.length + '')
|
||||
// editPrintElementData.printStyleList[props.type].overall = []
|
||||
editPrintElementData.printStyleList[props.type].overall.push(item)
|
||||
}
|
||||
setTimeout(()=>{
|
||||
editPrintElementDom.pingpuRef.updataList([
|
||||
{
|
||||
action: ACTIONS.ADD,
|
||||
data: item,
|
||||
},
|
||||
]);
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
const setPosition = ()=>{
|
||||
nextTick(()=>{
|
||||
@@ -337,16 +408,16 @@ export default defineComponent({
|
||||
if(!editPrintElementData.selectDetail.printObject.prints)return
|
||||
let state = true
|
||||
// editPrintElementData.stateOverallSingle = 'single'
|
||||
let arr:any = editPrintElementData.selectDetail.printObject.prints
|
||||
let arr:any = editPrintElementData.selectDetail.newDetail?.print || editPrintElementData.selectDetail.printObject.prints
|
||||
if(props.type == 'element'){
|
||||
arr = editPrintElementData.selectDetail.trims.prints
|
||||
arr = editPrintElementData.selectDetail.newDetail?.element || editPrintElementData.selectDetail.trims.prints
|
||||
}
|
||||
if(editPrintElementData.selectDetail.newDetail?.[editPrintElementData.currentDetailType]){
|
||||
arr = editPrintElementData.selectDetail.newDetail[editPrintElementData.currentDetailType]
|
||||
}
|
||||
if(arr && arr.length > 0){
|
||||
arr.forEach((item:any)=>{
|
||||
if(!item.ifSingle && arr.length == 1){
|
||||
if(!item.ifSingle){
|
||||
editPrintElementData.stateOverallSingle = 'overall',
|
||||
state = false
|
||||
}
|
||||
@@ -377,15 +448,42 @@ export default defineComponent({
|
||||
addPrintELement(newVal)
|
||||
}
|
||||
})
|
||||
watch(()=>((editPrintElementData.selectDetail?.undividedLayer || editPrintElementData.selectDetail?.id)),(newVal)=>{
|
||||
watch(()=>((editPrintElementData.selectDetail?.id)),(newVal)=>{
|
||||
if(!newVal)return
|
||||
editPrintElementData.isSketchLoad = false,
|
||||
editPrintElementData.printStyleList[props.type] = {
|
||||
single:[],
|
||||
overall:[],
|
||||
}
|
||||
if(!editPrintElementData.selectDetail.sketchMask){
|
||||
sketchToMask(editPrintElementData.selectDetail?.layersObject[0].maskUrl).then((res:any)=>{
|
||||
editPrintElementData.selectDetail.sketchMask = res
|
||||
})
|
||||
}
|
||||
setPosition()
|
||||
},{immediate: true,})
|
||||
watch(()=>editPrintElementData.stateOverallSingle,(newVal)=>{
|
||||
let arr = editPrintElementData.printStyleList[props.type][newVal]
|
||||
if(arr.length > 0){
|
||||
editPrintElementData.imgDomIndex = 0
|
||||
if(newVal == 'overall'){
|
||||
arr.forEach((item:any,index:number) => {
|
||||
item.id_ = index
|
||||
});
|
||||
}
|
||||
}else{
|
||||
editPrintElementData.imgDomIndex = -1
|
||||
}
|
||||
})
|
||||
watch(()=>editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle].length,(newVal)=>{
|
||||
if(newVal > 0){
|
||||
if(editPrintElementData.imgDomIndex < 0)editPrintElementData.imgDomIndex = 0
|
||||
}
|
||||
})
|
||||
const addOverallToCanvas = ()=>{
|
||||
|
||||
editPrintElementDom.pingpuRef
|
||||
}
|
||||
//设置移动
|
||||
const itemMoveMousedown = (index:number,event:any)=>{
|
||||
if (event.target.tagName === 'IMG' || event.target.nodeName === 'IMG')return
|
||||
@@ -649,7 +747,17 @@ export default defineComponent({
|
||||
}
|
||||
//排序 从大到小
|
||||
const sortDesignCollection = ()=> {
|
||||
|
||||
if(editPrintElementData.stateOverallSingle == 'overall'){
|
||||
let arr:any = editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle]
|
||||
let tokenList = arr.map((item)=>({token:item.token,srot:item.pattern.style.zIndex}))
|
||||
let tokens = tokenList.sort((a:any,b:any)=>a.srot - b.srot).map((item:any)=>item.token)
|
||||
editPrintElementDom.pingpuRef.updataList([
|
||||
{
|
||||
action: ACTIONS.SORT,
|
||||
tokens: tokens,
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
const reRange = (item:any, x:number, y:number,str?:string)=>{
|
||||
let elList:any = collItemSize.elList
|
||||
@@ -692,6 +800,14 @@ export default defineComponent({
|
||||
const navDelete = (item:any)=>{
|
||||
let arr:any = editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle]
|
||||
let index = arr.findIndex((arrItem:any)=>arrItem.uniqueId == item.uniqueId)
|
||||
if(editPrintElementData.stateOverallSingle == 'overall'){
|
||||
editPrintElementDom.pingpuRef.updataList([
|
||||
{
|
||||
action: ACTIONS.DELETE,
|
||||
token: arr[index].token,
|
||||
},
|
||||
]);
|
||||
}
|
||||
arr.splice(index,1)
|
||||
setItemPosition()
|
||||
}
|
||||
@@ -738,6 +854,106 @@ export default defineComponent({
|
||||
collItemSize.prentWidth = (collItemSize.padding + remValue) * elArr.length + 'px'
|
||||
moveItem()
|
||||
}
|
||||
const updateCanvas = (arr)=>{//画布操作更新对应的参数
|
||||
let list = editPrintElementData.printStyleList[props.type].overall
|
||||
arr.forEach((item)=>{
|
||||
const obj = list.find((v) => v.token === item.token);
|
||||
if (item.action === ACTIONS.UPDATE) {
|
||||
if (item.action === ACTIONS.UPDATE) {
|
||||
try {
|
||||
const key = item.key;
|
||||
const str = /^\[/.test(item.key)
|
||||
? "obj" + key
|
||||
: "obj." + key;
|
||||
eval(`${str} = item.value`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
} else if (item.action === ACTIONS.SELECT) {
|
||||
overallSetIndex(obj)
|
||||
}
|
||||
})
|
||||
}
|
||||
const inputFillAngle = (angle:any)=>{
|
||||
let arr = editPrintElementData.printStyleList[props.type].overall
|
||||
arr[editPrintElementData.imgDomIndex].angle = angle
|
||||
editPrintElementDom.pingpuRef.updataList([
|
||||
{
|
||||
action: ACTIONS.UPDATE,
|
||||
token: arr[editPrintElementData.imgDomIndex].token,
|
||||
key: 'angle',
|
||||
value: angle,
|
||||
},
|
||||
]);
|
||||
}
|
||||
const inputFillOffset = (offset:any)=>{
|
||||
let arr = editPrintElementData.printStyleList[props.type].overall
|
||||
arr[editPrintElementData.imgDomIndex].location = [offset.left * offset.size[0] / 100,offset.top * offset.size[1] / 100]
|
||||
editPrintElementDom.pingpuRef.updataList([
|
||||
{
|
||||
action: ACTIONS.UPDATE,
|
||||
token: arr[editPrintElementData.imgDomIndex].token,
|
||||
key: 'location[0]',
|
||||
value: offset.left,
|
||||
},
|
||||
]);
|
||||
editPrintElementDom.pingpuRef.updataList([
|
||||
{
|
||||
action: ACTIONS.UPDATE,
|
||||
token: arr[editPrintElementData.imgDomIndex].token,
|
||||
key: 'location[1]',
|
||||
value: offset.top,
|
||||
},
|
||||
]);
|
||||
// editPrintElementData.overallDetail.offsetX = offset.left
|
||||
// editPrintElementData.overallDetail.offsetY = offset.top
|
||||
}
|
||||
const inputFillScale = (scale:any)=>{
|
||||
let arr = editPrintElementData.printStyleList[props.type].overall
|
||||
arr[editPrintElementData.imgDomIndex].scale = [scale,scale]
|
||||
editPrintElementDom.pingpuRef.updataList([
|
||||
{
|
||||
action: ACTIONS.UPDATE,
|
||||
token: arr[editPrintElementData.imgDomIndex].token,
|
||||
key: 'scale[0]',
|
||||
value: scale,
|
||||
},
|
||||
]);
|
||||
}
|
||||
const inputFill_Gap = (x:any,y:any)=>{
|
||||
let arr = editPrintElementData.printStyleList[props.type].overall
|
||||
arr[editPrintElementData.imgDomIndex].object.gapX = x
|
||||
arr[editPrintElementData.imgDomIndex].object.gapY = y
|
||||
editPrintElementDom.pingpuRef.updataList([
|
||||
{
|
||||
action: ACTIONS.UPDATE,
|
||||
token: arr[editPrintElementData.imgDomIndex].token,
|
||||
key: "object.gapX",
|
||||
value: x,
|
||||
},
|
||||
]);
|
||||
editPrintElementDom.pingpuRef.updataList([
|
||||
{
|
||||
action: ACTIONS.UPDATE,
|
||||
token: arr[editPrintElementData.imgDomIndex].token,
|
||||
key: "object.gapY",
|
||||
value: y,
|
||||
},
|
||||
]);
|
||||
}
|
||||
const overallSetIndex = (item)=>{
|
||||
if(editPrintElementData.stateOverallSingle !== 'overall')return
|
||||
let arr = editPrintElementData.printStyleList[props.type].overall
|
||||
let index = arr.findIndex((arrItem:any)=>arrItem.token == item.token)
|
||||
editPrintElementData.imgDomIndex = index
|
||||
editPrintElementDom.pingpuRef.updataList([
|
||||
{
|
||||
action: ACTIONS.SELECT,
|
||||
token: arr[index].token,
|
||||
},
|
||||
]);
|
||||
}
|
||||
onMounted(()=>{
|
||||
if(props.type == 'element'){
|
||||
editPrintElementData.stateOverallSingle = 'single'
|
||||
@@ -747,6 +963,7 @@ export default defineComponent({
|
||||
previewDetailPrintData()
|
||||
})
|
||||
return{
|
||||
t,
|
||||
getMousePosition,
|
||||
...toRefs(editPrintElementDom),
|
||||
...toRefs(editPrintElementData),
|
||||
@@ -760,6 +977,12 @@ export default defineComponent({
|
||||
clearOverall,
|
||||
designMousedown,
|
||||
navDelete,
|
||||
updateCanvas,
|
||||
inputFillAngle,
|
||||
inputFillOffset,
|
||||
inputFillScale,
|
||||
inputFill_Gap,
|
||||
overallSetIndex,
|
||||
}
|
||||
},
|
||||
directives:{
|
||||
@@ -876,7 +1099,10 @@ export default defineComponent({
|
||||
.habit_System_Designer {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
justify-content: flex-end;
|
||||
margin-top: 1.8rem;
|
||||
margin-right: .8rem;
|
||||
|
||||
.ant-slider-track,
|
||||
.ant-slider-rail {
|
||||
background-color: #e1e1e1;
|
||||
@@ -924,6 +1150,9 @@ export default defineComponent({
|
||||
cursor: pointer;
|
||||
border-radius: 1rem;
|
||||
overflow: hidden;
|
||||
&.active{
|
||||
border: 2px solid #B4B4B4;
|
||||
}
|
||||
> img{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -977,13 +1206,23 @@ export default defineComponent({
|
||||
overflow-x: hidden;
|
||||
// max-height: 80%;
|
||||
position: relative;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
>img{
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
// height: 100%;
|
||||
max-width: 100%;
|
||||
// width: 100%;
|
||||
|
||||
&.designOpenrtion_sketchMask{
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.designOpenrtion_sketch_mask{
|
||||
z-index: 2;
|
||||
@@ -1013,8 +1252,13 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
.designOpenrtion_pingpu{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
.designOpenrtion_btn{
|
||||
z-index: 3;
|
||||
z-index: 99;
|
||||
>div{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
</div> -->
|
||||
<sketchType v-if="currentDetailType === 'sketch'"></sketchType>
|
||||
<!-- <printType v-if="currentDetailType === 'print'"></printType> -->
|
||||
<editPrintElement ref="editPrintElement" v-if="currentDetailType === 'print'" type="print"></editPrintElement>
|
||||
<editPrintElement ref="editPrintElement" v-if="currentDetailType === 'element'" type="element"></editPrintElement>
|
||||
<editPrintElement ref="editPrintElement" v-if="currentDetailType === 'print'" type="print" :sketchSize="sketchSize"></editPrintElement>
|
||||
<editPrintElement ref="editPrintElement" v-if="currentDetailType === 'element'" type="element" :sketchSize="sketchSize"></editPrintElement>
|
||||
<modelsType ref="modelsType" v-if="currentDetailType === 'models'"></modelsType>
|
||||
</div>
|
||||
|
||||
@@ -24,6 +24,12 @@ export default defineComponent({
|
||||
components:{
|
||||
sketchType,printType,editPrintElement,modelsType
|
||||
},
|
||||
props:{
|
||||
sketchSize:{
|
||||
type:Object,
|
||||
default:()=>{}
|
||||
},
|
||||
},
|
||||
setup(props,{emit}) {
|
||||
const store = useStore();
|
||||
const detailData = reactive({
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="repeat-setting">
|
||||
{{ }}
|
||||
<div class="repeat-setting-item">
|
||||
<span class="label">{{ t("Canvas.angle") }}</span>
|
||||
<angle-tool
|
||||
:angle="angle"
|
||||
styleType="2"
|
||||
@input="(e) => emit('inputFillAngle', e)"
|
||||
/>
|
||||
</div>
|
||||
<p></p>
|
||||
<div class="repeat-setting-item">
|
||||
<span class="label">{{ t("Canvas.scale") }}</span>
|
||||
<slider
|
||||
:min="1"
|
||||
:max="1000"
|
||||
:step="1"
|
||||
is-input
|
||||
:tipFormatter="(v) => `${scale}%`"
|
||||
:value="scale"
|
||||
@input="inputFillScale"
|
||||
/>
|
||||
</div>
|
||||
<p></p>
|
||||
<div class="repeat-setting-item">
|
||||
<span class="label">Gap X</span>
|
||||
<slider
|
||||
:min="0"
|
||||
:max="1000"
|
||||
:step="1"
|
||||
is-input
|
||||
:tipFormatter="(v) => `${v}px`"
|
||||
:value="gapX"
|
||||
@input="(e) => emit('inputFill_Gap', e, gapY)"
|
||||
@change="(e) => emit('changeFill_Gap', e, gapY)"
|
||||
/>
|
||||
</div>
|
||||
<p></p>
|
||||
<div class="repeat-setting-item">
|
||||
<span class="label">Gap Y</span>
|
||||
<slider
|
||||
:min="0"
|
||||
:max="1000"
|
||||
:step="1"
|
||||
is-input
|
||||
:tipFormatter="(v) => `${v}px`"
|
||||
:value="gapY"
|
||||
@input="(e) => emit('inputFill_Gap', gapX, e)"
|
||||
@change="(e) => emit('changeFill_Gap', gapX, e)"
|
||||
/>
|
||||
</div>
|
||||
<p></p>
|
||||
<div class="repeat-setting-item">
|
||||
<span class="label">{{ t("Canvas.offset") }}</span>
|
||||
<offset-tool
|
||||
:top="offset[1]"
|
||||
:left="offset[0]"
|
||||
@input="inputOffset"
|
||||
@change="(e) => emit('changeFillOffset', e)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, computed } from "vue";
|
||||
import AngleTool from "./tools/AngleTool.vue";
|
||||
import OffsetTool from "./tools/OffsetTool.vue";
|
||||
import Slider from "./tools/Slider.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
object: {
|
||||
required: true,
|
||||
type: Object,
|
||||
},
|
||||
sketchPath: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
});
|
||||
const angle = computed(() => props.object?.angle || 0);
|
||||
const scale = computed(() => {
|
||||
// let scaleValue = props.object?.scale/10;
|
||||
// return props.object?.scale/10;
|
||||
return props.object?.scale[0] * 100;
|
||||
});
|
||||
const offset = ref([0,0])
|
||||
const sketchSize:any = async ()=>{
|
||||
let img = new Image();
|
||||
let size = [0,0];
|
||||
img.src = props.sketchPath;
|
||||
await new Promise((resolve, reject) => {
|
||||
img.onload = () => {
|
||||
size = [img.width, img.height]
|
||||
resolve([img.width, img.height]);
|
||||
}
|
||||
img.onerror = reject;
|
||||
});
|
||||
return size
|
||||
}
|
||||
watch (() => props.object.path || props.object.location, async () => {
|
||||
let size = await sketchSize();
|
||||
offset.value[0] = props.object.location[0] / size[0] * 100;
|
||||
offset.value[1] = props.object.location[1] / size[1] * 100;
|
||||
},{immediate: true})
|
||||
const gapX = computed(() => props.object.object?.gapX || 0);
|
||||
const gapY = computed(() => props.object.object?.gapY || 0);
|
||||
const emit = defineEmits([
|
||||
"inputFillAngle",
|
||||
"changeFillAngle",
|
||||
"inputFillOffset",
|
||||
"changeFillOffset",
|
||||
"inputFillScale",
|
||||
"changeFillScale",
|
||||
"inputFill_Gap",
|
||||
"changeFill_Gap",
|
||||
]);
|
||||
const inputFillScale = (e) => {
|
||||
const scale = e / 100;
|
||||
console.log(scale)
|
||||
emit("inputFillScale", scale);
|
||||
};
|
||||
const inputOffset = async (e:any)=>{
|
||||
emit('inputFillOffset', {...e,size: await sketchSize()})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.repeat-setting {
|
||||
user-select: none;
|
||||
width: 228px;
|
||||
> .title {
|
||||
line-height: 35px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin-top: -12px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
> .repeat-setting-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&.offset {
|
||||
justify-content: center;
|
||||
}
|
||||
> .label {
|
||||
min-width: 68px;
|
||||
font-size: 12px;
|
||||
}
|
||||
&:not(.offset) > div {
|
||||
width: 120px;
|
||||
flex: 1;
|
||||
}
|
||||
> .slider {
|
||||
--slider-thumb-color1: #000;
|
||||
--slider-thumb-color2: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div class="angle-tool" :disabled="disabled">
|
||||
<template v-if="styleType === '1'">
|
||||
<div
|
||||
ref="dishRef"
|
||||
class="dish"
|
||||
@mousedown.stop="mousedown"
|
||||
@touchmove.stop="mousedown"
|
||||
>
|
||||
<div
|
||||
class="pointer"
|
||||
:style="{ transform: `rotate(${angle}deg)` }"
|
||||
>
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input
|
||||
type="number"
|
||||
v-model="angle"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<my-input
|
||||
v-if="styleType === '2'"
|
||||
v-model="angle"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
:disabled="disabled"
|
||||
type="number"
|
||||
after="°"
|
||||
icon="icon-angle"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, watch } from "vue";
|
||||
import { calculateAngle } from "@/component/Canvas/CanvasEditor/utils/helper";
|
||||
import MyInput from "./MyInput.vue";
|
||||
// Props
|
||||
const props = defineProps({
|
||||
styleType: {
|
||||
type: String,
|
||||
default: "1",
|
||||
},
|
||||
angle: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["change", "input"]);
|
||||
const angle = ref(props.angle);
|
||||
watch(
|
||||
() => props.angle,
|
||||
(value) => {
|
||||
angle.value = value;
|
||||
}
|
||||
);
|
||||
const dishRef = ref<HTMLDivElement>();
|
||||
const mousedown = (e: MouseEvent | TouchEvent) => {
|
||||
if (props.disabled) return;
|
||||
const mousemove = (e: MouseEvent | TouchEvent) => {
|
||||
if (!dishRef.value) return;
|
||||
const { left, top, width, height } =
|
||||
dishRef.value.getBoundingClientRect();
|
||||
const centerX = left + width / 2;
|
||||
const centerY = top + height / 2;
|
||||
const { clientX, clientY } = e?.touches?.[0] || e;
|
||||
angle.value = calculateAngle(centerX, centerY, clientX, clientY, true);
|
||||
onInput();
|
||||
};
|
||||
mousemove(e);
|
||||
const mouseup = () => {
|
||||
onChange();
|
||||
document.removeEventListener("mousemove", mousemove);
|
||||
document.removeEventListener("touchmove", mousemove);
|
||||
document.removeEventListener("mouseup", mouseup);
|
||||
document.removeEventListener("touchend", mouseup);
|
||||
};
|
||||
document.addEventListener("mousemove", mousemove);
|
||||
document.addEventListener("touchmove", mousemove);
|
||||
document.addEventListener("mouseup", mouseup);
|
||||
document.addEventListener("touchend", mouseup);
|
||||
};
|
||||
const onInput = () => !props.disabled && emit("input", angle.value);
|
||||
var changeTime: any = null;
|
||||
const onChange = () => {
|
||||
if (props.disabled) return;
|
||||
clearTimeout(changeTime);
|
||||
changeTime = setTimeout(() => emit("change", angle.value), 500);
|
||||
};
|
||||
// var angleTime = null;
|
||||
// watch(angle, (value) => {
|
||||
// emit("input", value);
|
||||
// clearTimeout(angleTime);
|
||||
// angleTime = setTimeout(() => emit("change", value), 50);
|
||||
// });
|
||||
// defineExpose({
|
||||
// open,
|
||||
// close,
|
||||
// });
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.angle-tool {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
--color: #000;
|
||||
&[disabled="true"] {
|
||||
--color: #b2b2b2;
|
||||
> .dish {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
> .dish {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 1px solid var(--color);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
> .pointer {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
> span {
|
||||
position: absolute;
|
||||
top: 10%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
width: 35%;
|
||||
height: 35%;
|
||||
background-color: var(--color);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .input {
|
||||
margin-left: 5px;
|
||||
font-size: 14px;
|
||||
color: var(--color);
|
||||
flex: 1;
|
||||
// min-width: 45px;
|
||||
// max-width: 80px;
|
||||
// width: 50px;
|
||||
> input {
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
> .my-input {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="my-input">
|
||||
<span class="decorate"></span>
|
||||
<span v-show="icon" :class="['iconfont', icon]"></span>
|
||||
<span v-show="before" class="before">{{ before }}</span>
|
||||
<input v-bind="$attrs" :value="modelValue" @input="onInput" />
|
||||
<span v-show="after" class="after">{{ after }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, watch } from "vue";
|
||||
const props = defineProps({
|
||||
modelValue: { type: Number, default: 0 },
|
||||
icon: { default: "", type: String },
|
||||
before: { default: "", type: String },
|
||||
after: { default: "", type: String },
|
||||
});
|
||||
const emit = defineEmits(["update:modelValue", "input"]);
|
||||
const onInput = (e) => {
|
||||
const value = e.target.value;
|
||||
emit("update:modelValue", value);
|
||||
emit("input", value);
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.my-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
border: 1px solid rgba(230, 230, 231, 1);
|
||||
border-radius: 3px;
|
||||
height: 20px;
|
||||
padding: 0 4px 0 2px;
|
||||
> .decorate {
|
||||
width: 2px;
|
||||
background-color: rgba(230, 230, 231, 1);
|
||||
border-radius: 3px;
|
||||
height: 85%;
|
||||
margin-right: 4px;
|
||||
}
|
||||
> .iconfont {
|
||||
font-size: 10px;
|
||||
color: #000;
|
||||
margin-right: 2px;
|
||||
}
|
||||
> .before {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
margin-right: 2px;
|
||||
}
|
||||
> .after {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
}
|
||||
> input {
|
||||
font-size: 12px;
|
||||
width: 0;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
outline: none;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<a-select
|
||||
class="my-select"
|
||||
:size="size"
|
||||
@change="change"
|
||||
:defaultValue="defaultValue"
|
||||
@dropdownVisibleChange="dropdownVisibleChange"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="v in list"
|
||||
:key="v.value"
|
||||
:value="v.value"
|
||||
:title="v.tip"
|
||||
@mouseover.stop.prevent="mouseover(v)"
|
||||
@mouseleave="mouseleave(v)"
|
||||
>{{ v.label }}</a-select-option
|
||||
>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, watch } from "vue";
|
||||
const props = defineProps({
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
defaultValue: {
|
||||
default: "",
|
||||
},
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: "small",
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["change", "active"]);
|
||||
const isChange = ref(false);
|
||||
const initValue = ref(props.defaultValue);
|
||||
const activeValue = ref(props.defaultValue);
|
||||
const timeout = ref(null);
|
||||
const mouseover = (v) => {
|
||||
clearTimeout(timeout.value);
|
||||
if (v.value === activeValue.value) return;
|
||||
emit("active", v.value, activeValue.value);
|
||||
activeValue.value = v.value;
|
||||
};
|
||||
const mouseleave = () => {
|
||||
clearTimeout(timeout.value);
|
||||
timeout.value = setTimeout(() => {
|
||||
dropdownVisibleChange(false);
|
||||
}, 100);
|
||||
};
|
||||
const change = (v) => {
|
||||
isChange.value = true;
|
||||
emit("change", v, initValue.value);
|
||||
};
|
||||
const dropdownVisibleChange = (v) => {
|
||||
if (v) {
|
||||
isChange.value = false;
|
||||
initValue.value = props.defaultValue;
|
||||
} else if (!isChange.value) {
|
||||
emit("active", initValue.value, activeValue.value);
|
||||
activeValue.value = initValue.value;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<div class="offset-tool">
|
||||
<div class="input" v-show="showInput">
|
||||
<my-input
|
||||
v-model="left"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
type="number"
|
||||
before="X"
|
||||
after="%"
|
||||
:min="-100"
|
||||
:max="100"
|
||||
/>
|
||||
<my-input
|
||||
v-model="top"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
type="number"
|
||||
before="Y"
|
||||
after="%"
|
||||
:min="-100"
|
||||
:max="100"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="dish"
|
||||
@mousedown="mousedown"
|
||||
@touchstart="mousedown"
|
||||
ref="dishRef"
|
||||
v-show="showDish"
|
||||
>
|
||||
<img src="/src/assets/images/icon/xyz.png" />
|
||||
<span class="ball" :style="ballStyle"></span>
|
||||
<span class="tip x">X: {{ left }}%</span>
|
||||
<span class="tip y">Y: {{ top }}%</span>
|
||||
<span class="line x"></span>
|
||||
<span class="line y"></span>
|
||||
<span class="line z" :style="lineZStyle"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, watch, computed } from "vue";
|
||||
import MyInput from "./MyInput.vue";
|
||||
const props = defineProps({
|
||||
left: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
top: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
showInput: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showDish: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["change", "input"]);
|
||||
// 工具的实际坐标 -100 ~ 100
|
||||
const top = ref(Math.round(props.top));
|
||||
const left = ref(Math.round(props.left));
|
||||
|
||||
// 原点的坐标 0 ~ 100
|
||||
const ballStyle = computed(() => ({
|
||||
top: 50 + Number(top.value) / 2 + "%",
|
||||
left: 50 + Number(left.value) / 2 + "%",
|
||||
}));
|
||||
watch(
|
||||
() => props.left,
|
||||
(v) => (left.value = Math.round(v))
|
||||
);
|
||||
watch(
|
||||
() => props.top,
|
||||
(v) => (top.value = Math.round(v))
|
||||
);
|
||||
const dishRef = ref<HTMLDivElement>();
|
||||
const mousedown = (e: MouseEvent | TouchEvent) => {
|
||||
if (!dishRef.value) return;
|
||||
const mousemove = (e: MouseEvent | TouchEvent) => {
|
||||
if (!dishRef.value) return;
|
||||
const rect = dishRef.value.getBoundingClientRect();
|
||||
const X = e.clientX || (e as TouchEvent).touches[0].clientX;
|
||||
const Y = e.clientY || (e as TouchEvent).touches[0].clientY;
|
||||
var x = ((X - rect.left) / rect.width) * 100;
|
||||
var y = ((Y - rect.top) / rect.height) * 100;
|
||||
if (x < 0) x = 0;
|
||||
if (x > 100) x = 100;
|
||||
if (y < 0) y = 0;
|
||||
if (y > 100) y = 100;
|
||||
left.value = Math.round((x - 50) * 2);
|
||||
top.value = Math.round((y - 50) * 2);
|
||||
onInput();
|
||||
};
|
||||
mousemove(e);
|
||||
const mouseup = () => {
|
||||
onChange();
|
||||
document.removeEventListener("mousemove", mousemove);
|
||||
document.removeEventListener("touchmove", mousemove);
|
||||
document.removeEventListener("mouseup", mouseup);
|
||||
document.removeEventListener("touchend", mouseup);
|
||||
};
|
||||
document.addEventListener("mousemove", mousemove);
|
||||
document.addEventListener("touchmove", mousemove);
|
||||
document.addEventListener("mouseup", mouseup);
|
||||
document.addEventListener("touchend", mouseup);
|
||||
};
|
||||
const onInput = () => {
|
||||
emit("input", { left: left.value, top: top.value });
|
||||
};
|
||||
var changeTime: any = null;
|
||||
const onChange = () => {
|
||||
clearTimeout(changeTime);
|
||||
changeTime = setTimeout(() => {
|
||||
emit("change", {
|
||||
left: left.value,
|
||||
top: top.value,
|
||||
});
|
||||
}, 500);
|
||||
};
|
||||
const lineZStyle = computed(() => ({
|
||||
"--rotateZ": calculateAngle(0, 0, left.value, top.value) + "deg",
|
||||
width: calculateDistance(0, 0, left.value, top.value) / 2 + "%",
|
||||
}));
|
||||
// 计算角度
|
||||
function calculateAngle(x1: number, y1: number, x2: number, y2: number) {
|
||||
const deltaX = x2 - x1;
|
||||
const deltaY = y1 - y2;
|
||||
let angle = Math.atan2(deltaX, deltaY) * (180 / Math.PI) - 90;
|
||||
return angle;
|
||||
}
|
||||
// 计算距离
|
||||
function calculateDistance(x1: number, y1: number, x2: number, y2: number) {
|
||||
const deltaX = x2 - x1;
|
||||
const deltaY = y2 - y1;
|
||||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
return distance;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.offset-tool {
|
||||
position: relative;
|
||||
> .input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
> * {
|
||||
flex: 1;
|
||||
margin-right: 12px;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .dish {
|
||||
width: 135px;
|
||||
height: 135px;
|
||||
border: 1px solid #eaeaea;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
background-color: #f6f6f6;
|
||||
margin-top: 24px;
|
||||
> * {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
> img {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
bottom: 4px;
|
||||
right: 4px;
|
||||
}
|
||||
> .ball {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border: 1px solid #fff;
|
||||
background-color: #333;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0px 0.68px 1.7px 0px rgba(0, 0, 0, 0.26);
|
||||
}
|
||||
> .tip {
|
||||
font-size: 10px;
|
||||
color: #000;
|
||||
line-height: 24px;
|
||||
&.x {
|
||||
top: 50%;
|
||||
right: 0%;
|
||||
transform: translate(100%, -50%);
|
||||
padding-left: 6px;
|
||||
}
|
||||
&.y {
|
||||
top: 0%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -100%);
|
||||
}
|
||||
}
|
||||
> .line {
|
||||
border-color: #d9d9d9;
|
||||
border-style: dashed;
|
||||
border-width: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
&.x {
|
||||
width: 100%;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
&.y {
|
||||
height: 100%;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
&.z {
|
||||
width: 50%;
|
||||
border-top-width: 1px;
|
||||
border-color: #454754;
|
||||
transform: translate(0%, -50%) rotateZ(var(--rotateZ));
|
||||
transform-origin: left center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
173
src/component/Detail/detailRight/overallSetting/tools/Slider.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<div class="slider" :disabled="disabled">
|
||||
<div
|
||||
class="input-range"
|
||||
:style="{
|
||||
'--progress': (value - props.min) / (props.max - props.min),
|
||||
}"
|
||||
>
|
||||
<span class="tip">{{ props.tipFormatter(value) }}</span>
|
||||
<input
|
||||
type="range"
|
||||
v-model="value"
|
||||
:min="props.min"
|
||||
:max="props.max"
|
||||
:step="props.step"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</div>
|
||||
<div class="input" v-show="isInput">
|
||||
<my-input
|
||||
v-model="value"
|
||||
:min="props.min"
|
||||
:max="props.max"
|
||||
:step="props.step"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
:disabled="disabled"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, watch } from "vue";
|
||||
import MyInput from "./MyInput.vue";
|
||||
const props = defineProps({
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
min: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
default: 100,
|
||||
},
|
||||
step: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
tipFormatter: {
|
||||
type: Function,
|
||||
default: (v) => v,
|
||||
},
|
||||
isInput: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["change", "input"]);
|
||||
const value = ref(props.value);
|
||||
watch(
|
||||
() => props.value,
|
||||
(v) => (value.value = v)
|
||||
);
|
||||
const onInput = () => !props.disabled && emit("input", Number(value.value));
|
||||
var changeTime: any = null;
|
||||
const onChange = () => {
|
||||
if (props.disabled) return;
|
||||
clearTimeout(changeTime);
|
||||
changeTime = setTimeout(() => emit("change", Number(value.value)), 500);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.slider {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 150px;
|
||||
--input-thumb-size: 10px;
|
||||
--backcolor1: var(--slider-thumb-color1, #4285f4);
|
||||
--backcolor2: var(--slider-thumb-color2, rgba(0, 0, 0, 0.1));
|
||||
&:hover {
|
||||
> .input-range > .tip {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
> .input-range {
|
||||
position: relative;
|
||||
flex: 2;
|
||||
> input {
|
||||
width: 100%;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: 5px;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
var(--backcolor1) 0%,
|
||||
var(--backcolor1) calc(var(--progress) * 100%),
|
||||
var(--backcolor2) calc(var(--progress) * 100%),
|
||||
var(--backcolor2) 100%
|
||||
);
|
||||
&::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: var(--input-thumb-size);
|
||||
height: var(--input-thumb-size);
|
||||
border-radius: 50%;
|
||||
background: var(--backcolor1); /* 蓝色滑块 */
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
&::-webkit-slider-thumb:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
> .tip {
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
color: #666;
|
||||
top: 0;
|
||||
left: calc(
|
||||
(100% - var(--input-thumb-size)) * var(--progress) +
|
||||
var(--input-thumb-size) / 2
|
||||
);
|
||||
transform: translate(-50%, -100%);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
color: white;
|
||||
padding: 0.4rem 0.8rem;
|
||||
border-radius: 0.4rem;
|
||||
font-size: 1.2rem;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 97%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 5px solid rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
> .input {
|
||||
flex: 1;
|
||||
margin-left: 10px;
|
||||
> input {
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,9 +4,9 @@
|
||||
<div class="back" v-show="isEditPattern.value">
|
||||
<i class="fi fi-br-angle-left" @click="setBack"></i>
|
||||
</div>
|
||||
<modelNav @canvasReload="()=>$emit('canvasReload')" @addSketch="()=>$emit('addSketch')" @sketchSysToLibrary="()=>$emit('sketchSysToLibrary')" @deleteItem="deleteItem"></modelNav>
|
||||
<modelNav ref="modelNav" @addSketch="()=>$emit('addSketch')" @sketchSysToLibrary="()=>$emit('sketchSysToLibrary')" @deleteItem="deleteItem"></modelNav>
|
||||
</div>
|
||||
<div class="modelindex_right">
|
||||
<div class="modelindex_right" ref="modelindexRight">
|
||||
<div class="detail_btn">
|
||||
<!-- 全屏 -->
|
||||
<!-- <i class="fi fi-bs-expand-arrows-alt" @click="showDesignImgDetail('2')"></i> -->
|
||||
@@ -14,7 +14,7 @@
|
||||
<i class="icon iconfont icon-chehui" @click="revocation"></i>
|
||||
<i class="icon iconfont icon-fanchehui" @click="oppositeRevocation"></i>
|
||||
<!-- 编辑 -->
|
||||
<i class="fi fi-rs-pencil-paintbrush" :title="$t('DesignDetail.editSketchTitle')" :class="{'pointerEventsNone':!selectDetail?.id}" @click="()=>$emit('addDetail')"></i>
|
||||
<i class="fi fi-rs-pencil-paintbrush" :title="$t('DesignDetail.editSketchTitle')" :class="{'pointerEventsNone':!selectDetail?.id,active:isEditPattern.value == 'editSketch'}" @click="showDesignImgDetail('editSketch')"></i>
|
||||
<i class="fi fi-rr-edit" :title="$t('DesignDetail.editTitle')" :class="{active:isEditPattern.value == 'canvasEditor','pointerEventsNone':!selectDetail?.id}" @click="showDesignImgDetail('canvasEditor')"></i>
|
||||
|
||||
<i class="icon iconfont icon-clothes" :title="$t('Canvas.editFrontBack')" style="font-size: 3.2rem;" @click="showDesignImgDetail('redGreenExample')" :class="{active:isEditPattern.value == 'redGreenExample','pointerEventsNone':!selectDetail?.id}"></i>
|
||||
@@ -32,7 +32,7 @@
|
||||
</i>
|
||||
|
||||
</div>
|
||||
<position ref="position" @modelOnLoad="()=>$emit('modelOnLoad')" @canvasReload="()=>$emit('canvasReload')" @addSketch="()=>$emit('addSketch')" :imgDesignImg=imgDesignImg></position>
|
||||
<position ref="position" @addSketch="()=>$emit('addSketch')" :imgDesignImg=imgDesignImg></position>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -51,7 +51,7 @@ export default defineComponent({
|
||||
components:{
|
||||
position,modelNav
|
||||
},
|
||||
emits:['detailEdit','canvasReload','addSketch','revocation','oppositeRevocation','modelOnLoad','sketchSysToLibrary','addDetail'],
|
||||
emits:['detailEdit','addSketch','revocation','oppositeRevocation','sketchSysToLibrary'],
|
||||
setup(props,{emit}) {
|
||||
const {t} = useI18n()
|
||||
const store = useStore();
|
||||
@@ -68,36 +68,40 @@ export default defineComponent({
|
||||
const getDetailListDom = reactive({
|
||||
libraryList:null as any,
|
||||
position:null as any,
|
||||
modelindexRight:null as any,
|
||||
modelNav:null as any,
|
||||
})
|
||||
const getSubmitData = (value:any,boolean)=>{
|
||||
return getDetailListDom.position.getSubmitData(value,boolean)
|
||||
const getSubmitData = (value:any)=>{
|
||||
return getDetailListDom.position.getSubmitData(value,)
|
||||
}
|
||||
const showDesignImgDetail = (str:any)=>{
|
||||
new Promise((resolve, reject) => {
|
||||
if(
|
||||
getDetailListData.isEditPattern.value&&
|
||||
detailData?.getCanvasIfEdit?.fun&&detailData?.getCanvasIfEdit?.fun() > 0
|
||||
){
|
||||
Modal.confirm({
|
||||
title: t('collectionModal.jsContent2'),
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
okText: 'Yes',
|
||||
cancelText: 'No',
|
||||
mask:false,
|
||||
centered:true,
|
||||
onOk() {
|
||||
resolve(true)
|
||||
emit('detailEdit',str)
|
||||
},
|
||||
onCancel(){
|
||||
resolve(false)
|
||||
}
|
||||
});
|
||||
}else{
|
||||
resolve(true)
|
||||
emit('detailEdit',str)
|
||||
}
|
||||
})
|
||||
emit('detailEdit',str)
|
||||
|
||||
// new Promise((resolve, reject) => {
|
||||
// if(
|
||||
// getDetailListData.isEditPattern.value&&
|
||||
// detailData?.getCanvasIfEdit?.fun&&detailData?.getCanvasIfEdit?.fun() > 0
|
||||
// ){
|
||||
// Modal.confirm({
|
||||
// title: t('collectionModal.jsContent2'),
|
||||
// icon: createVNode(ExclamationCircleOutlined),
|
||||
// okText: 'Yes',
|
||||
// cancelText: 'No',
|
||||
// mask:false,
|
||||
// centered:true,
|
||||
// onOk() {
|
||||
// resolve(true)
|
||||
// emit('detailEdit',str)
|
||||
// },
|
||||
// onCancel(){
|
||||
// resolve(false)
|
||||
// }
|
||||
// });
|
||||
// }else{
|
||||
// resolve(true)
|
||||
// emit('detailEdit',str)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
const deleteItem = ()=>{
|
||||
setRevocation()
|
||||
@@ -121,18 +125,25 @@ export default defineComponent({
|
||||
clearTimeout(time)
|
||||
time = setTimeout(()=>{
|
||||
store.commit('DesignDetail/setDesignDetail',getDetailListData.designDetail)
|
||||
getDetailListDom.position.updataPosition()
|
||||
|
||||
},1000)
|
||||
getDetailListDom.position?.updataPosition?.()
|
||||
getDetailListDom.modelNav?.setItemPosition?.()
|
||||
getDetailListDom.position?.updateRect?.()
|
||||
},500)
|
||||
}
|
||||
const setBack = ()=>{
|
||||
emit('detailEdit')
|
||||
showDesignImgDetail(getDetailListData.isEditPattern.value)
|
||||
}
|
||||
let observers = null as any
|
||||
onMounted(()=>{
|
||||
window.addEventListener('resize', handleResize);
|
||||
observers = new ResizeObserver(entries => {
|
||||
for (let entry of entries) {
|
||||
handleResize()
|
||||
}
|
||||
});
|
||||
observers.observe(getDetailListDom.modelindexRight);
|
||||
})
|
||||
onBeforeUnmount(()=>{
|
||||
window.removeEventListener('resize', handleResize);
|
||||
observers.disconnect();
|
||||
})
|
||||
return{
|
||||
...toRefs(detailData),
|
||||
|
||||
@@ -34,7 +34,7 @@ export default defineComponent({
|
||||
components:{
|
||||
position,
|
||||
},
|
||||
emits:['canvasReload','addSketch','deleteItem','sketchSysToLibrary'],
|
||||
emits:['addSketch','deleteItem','sketchSysToLibrary'],
|
||||
props:{
|
||||
},
|
||||
setup(props,{emit}) {
|
||||
@@ -79,25 +79,25 @@ export default defineComponent({
|
||||
detailData.selectDetail?.id &&
|
||||
detailData?.getCanvasIfEdit?.fun&&detailData?.getCanvasIfEdit?.fun() > 0
|
||||
){
|
||||
Modal.confirm({
|
||||
title: t('collectionModal.jsContent6'),
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
okText: 'Yes',
|
||||
cancelText: 'No',
|
||||
mask:false,
|
||||
centered:true,
|
||||
onOk() {
|
||||
resolve(true)
|
||||
emit('canvasReload')
|
||||
},
|
||||
onCancel(){
|
||||
resolve(false)
|
||||
}
|
||||
});
|
||||
resolve()
|
||||
// Modal.confirm({
|
||||
// title: t('collectionModal.jsContent6'),
|
||||
// icon: createVNode(ExclamationCircleOutlined),
|
||||
// okText: 'Yes',
|
||||
// cancelText: 'No',
|
||||
// mask:false,
|
||||
// centered:true,
|
||||
// onOk() {
|
||||
// resolve(true)
|
||||
// emit('canvasReload')
|
||||
// },
|
||||
// onCancel(){
|
||||
// resolve(false)
|
||||
// }
|
||||
// });
|
||||
}else{
|
||||
resolve(true)
|
||||
if(detailData.selectDetail.id !== item.id){
|
||||
emit('canvasReload')
|
||||
}
|
||||
}
|
||||
}).then((rv)=>{
|
||||
@@ -117,7 +117,6 @@ export default defineComponent({
|
||||
const maxObj = detailData.designDetail.clothes.find(item => item.priority === maxValue);
|
||||
store.commit('DesignDetail/setDesignColthes',maxObj.id)
|
||||
}
|
||||
emit('canvasReload')
|
||||
emit('deleteItem')
|
||||
}
|
||||
const addSketch = ()=>{
|
||||
@@ -268,17 +267,8 @@ export default defineComponent({
|
||||
let allPostition = store.state.Workspace.workspaceAllPosition
|
||||
return allPostition.find(item => item.value === type)?.name
|
||||
}
|
||||
let observers = null as any
|
||||
onMounted(()=>{
|
||||
observers = new ResizeObserver(entries => {
|
||||
for (let entry of entries) {
|
||||
setItemPosition()
|
||||
}
|
||||
});
|
||||
observers.observe(detailData.modelNavBox);
|
||||
})
|
||||
|
||||
onBeforeUnmount(()=>{
|
||||
observers.disconnect();
|
||||
})
|
||||
return{
|
||||
...toRefs(detailData),
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
<template>
|
||||
<div class="molepositon" :class="{active:!imgDesignImg}">
|
||||
<div class="designOpenrtion_imgMask" v-if="frontBack?.body?.path" :style="frontBack?.body?.style">
|
||||
<div class="designOpenrtion_print" v-for="item,index in frontBack.back" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))" @click="setpitch(item,index)" :style="frontBack.front[index].style">
|
||||
<div class="designOpenrtion_print" v-for="item,index in frontBack.back" :style="frontBack.front[index].style">
|
||||
<img :style="item.imageUrl?'':'display:none;'" :src="item.imageUrl" alt="">
|
||||
</div>
|
||||
<img class="perview_img" @load="setPrintSize()" ref="detailBody" :key="designDetail.designItemId" :src="frontBack?.body?.path" :style="'width:'+ frontBack?.body?.layersObject?.[0].imageSize?.[0] +';height:' + frontBack?.body?.layersObject?.[0].imageSize?.[0] +';'">
|
||||
<div class="detail_modal_item_front" v-for="item,index in frontBack.front" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))" @click="setpitch(item,index)" :style="item.style">
|
||||
<!-- <div class="detail_modal_item_front" ref="target" v-for="item,index in frontBack.front" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))" @click="setpitch(item,index)" :style="item.style">
|
||||
<img :src="item.imageUrl" alt="">
|
||||
</div> -->
|
||||
<div class="detail_modal_item_front" :ref="el => { setElementRef(el, index) }" v-for="item,index in frontBack.front" :class="{'active':item.id == selectDetail?.id}" :style="item.style">
|
||||
<img :src="item.imageUrl" alt="">
|
||||
</div>
|
||||
<div class="designOpenrtion_btnBox">
|
||||
<ul v-for="item,index in frontBack.front" :key="item" :class="{active:item.designOpenrtionBtn}" class="designOpenrtion_btn" :style="item.style" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))">
|
||||
<li class="designOpenrtion_btn_top" @mousedown.stop="itemSizeMousedown('top',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('top',getMousePosition($event,true))"></li>
|
||||
<li class="designOpenrtion_btn_bottom" @mousedown.stop="itemSizeMousedown('bottom',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('bottom',getMousePosition($event,true))"></li>
|
||||
<li class="designOpenrtion_btn_left" @mousedown.stop="itemSizeMousedown('left',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('left',getMousePosition($event,true))"></li>
|
||||
<li class="designOpenrtion_btn_right" @mousedown.stop="itemSizeMousedown('right',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('right',getMousePosition($event,true))"></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div ref="moveableContainer" class="moveableContainer"></div>
|
||||
</div>
|
||||
<div class="designOpenrtion_imgMask" v-if="!frontBack?.body?.path">
|
||||
<img :src="selectDetail?.undividedLayerWithSinglePrint || selectDetail?.undividedLayer || selectDetail?.path" style="object-fit: cover;" alt="">
|
||||
@@ -40,8 +36,10 @@ import { Https } from "@/tool/https";
|
||||
import { useStore } from "vuex";
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { getMousePosition } from "@/tool/mdEvent";
|
||||
import { Modal,message } from 'ant-design-vue';
|
||||
import newFollowVue from '@/component/Account/message/newFollow.vue';
|
||||
import Vue3Moveable from 'vue3-moveable';
|
||||
import Moveable from 'moveable';
|
||||
import { parse } from 'vue/compiler-sfc';
|
||||
import { scale } from 'echarts/types/src/scale/helper.js';
|
||||
export default defineComponent({
|
||||
components:{
|
||||
},
|
||||
@@ -51,7 +49,7 @@ export default defineComponent({
|
||||
type:Boolean,
|
||||
}
|
||||
},
|
||||
emits:['canvasReload','addSketch','modelOnLoad'],
|
||||
emits:['addSketch'],
|
||||
setup(props,{emit}) {
|
||||
const {t} = useI18n()
|
||||
const store = useStore();
|
||||
@@ -75,10 +73,7 @@ export default defineComponent({
|
||||
imgDom:null as any,
|
||||
direction:'',
|
||||
})
|
||||
watch(()=>selectItem.selectDetail,(newValue,oldValue)=>{
|
||||
if(!newValue && newValue?.id == oldValue?.id)return
|
||||
selectItem.imgDomIndex = detailData.frontBack.front.findIndex((item:any)=>item.id == newValue.id)
|
||||
},{immediate: true,})
|
||||
|
||||
watch(()=>detailData.frontBack?.body?.path,(newVal)=>{
|
||||
setPrintSize()
|
||||
})
|
||||
@@ -99,7 +94,6 @@ export default defineComponent({
|
||||
if(entries[0].contentRect.width == 0)return
|
||||
detailData.observerWH.width = Math.floor(entries[0].contentRect.width)
|
||||
detailData.observerWH.height = Math.floor(entries[0].contentRect.height)
|
||||
console.log(detailData.observerWH)
|
||||
})
|
||||
detailData.observer.observe(dom)
|
||||
|
||||
@@ -131,6 +125,7 @@ export default defineComponent({
|
||||
}
|
||||
for (const key in detailData.frontBack.back[index].style) {
|
||||
if(key == 'zIndex')return
|
||||
if(key == 'transform')return
|
||||
let value = detailData.frontBack.back[index].style[key]
|
||||
if(typeof value !== 'number'){
|
||||
value = value.replace('px','')
|
||||
@@ -142,7 +137,7 @@ export default defineComponent({
|
||||
}
|
||||
});
|
||||
setTimeout(() => {
|
||||
emit('modelOnLoad')
|
||||
initMoveableForSelected()
|
||||
},500);
|
||||
};
|
||||
img.src = detailData.frontBack?.body?.path;
|
||||
@@ -150,203 +145,280 @@ export default defineComponent({
|
||||
}
|
||||
const getDetailListDom = reactive({
|
||||
libraryList:null as any,
|
||||
moveableContainer:null as any,//控件层
|
||||
})
|
||||
|
||||
//设置尺寸
|
||||
const itemSizeMousedown = (direction:any,event:any)=>{
|
||||
selectItem.direction = direction
|
||||
selectItem.imgDom = document.getElementsByClassName('molepositon')[0].getElementsByClassName("detail_modal_item_front")[selectItem.imgDomIndex]
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].designOpenrtionBtn = true
|
||||
let imgDomWH = selectItem.imgDom.getBoundingClientRect()
|
||||
let li = (document.getElementsByClassName('molepositon')[0].getElementsByClassName("designOpenrtion_btn_top")[0] as any).offsetWidth/2
|
||||
if(selectItem.direction == 'right' || selectItem.direction == 'bottom'){
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].centers.left = imgDomWH.x+event.offsetX-li
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].centers.top = imgDomWH.y+event.offsetY-li
|
||||
}else{
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].centers.left = imgDomWH.x+event.offsetX+imgDomWH.width-li
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].centers.top = imgDomWH.y+event.offsetY+imgDomWH.height-li
|
||||
const elementRefs = ref([]) as any;
|
||||
const moveableInstance = ref(null) as any;
|
||||
const setElementRef = (el, index) => {
|
||||
elementRefs.value[index] = el;
|
||||
};
|
||||
const updateRect = ()=>{
|
||||
setTimeout(() => {
|
||||
moveableInstance.value.updateRect()
|
||||
}, 500);
|
||||
}
|
||||
const initMoveableForSelected = () => {
|
||||
// 销毁旧的实例
|
||||
if(selectItem.imgDomIndex == -1)return
|
||||
if (moveableInstance.value) {
|
||||
moveableInstance.value.destroy();
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', sizeMouseMove);
|
||||
document.addEventListener('touchmove', sizeTouchmove);
|
||||
document.addEventListener('mouseup', sizeMouseup);
|
||||
document.addEventListener('touchend', sizeMouseup);
|
||||
}
|
||||
const sizeMouseMove = (event:any)=>{
|
||||
let e = getMousePosition(event,false)
|
||||
sizeMouseMoveOperation(e)
|
||||
}
|
||||
const sizeTouchmove = (event:any)=>{
|
||||
let e = getMousePosition(event,true)
|
||||
sizeMouseMoveOperation(e)
|
||||
|
||||
}
|
||||
const sizeMouseup = (e:any)=>{
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].style={
|
||||
right:'auto',
|
||||
left:selectItem.imgDom.offsetLeft+'px',
|
||||
bottom:'auto',
|
||||
top:selectItem.imgDom.offsetTop+'px',
|
||||
height:selectItem.imgDom.offsetHeight+'px',
|
||||
width:selectItem.imgDom.offsetWidth+'px',
|
||||
zIndex:selectItem.imgDom.style.zIndex,
|
||||
// zIndex:selectItem.printZIndex
|
||||
const selectedEl = elementRefs.value[selectItem.imgDomIndex];
|
||||
if (!selectedEl) return;
|
||||
if(!selectedEl.style.left)return
|
||||
moveableInstance.value = new Moveable(getDetailListDom.moveableContainer, {
|
||||
target: selectedEl,
|
||||
draggable: true,
|
||||
scalable: true,
|
||||
rotatable: true,
|
||||
keepRatio: false, // 等比缩放
|
||||
snappable: true,
|
||||
snapThreshold: 5,
|
||||
edge: false,
|
||||
});
|
||||
let startPosition = {//记录初始位置
|
||||
left: 0,
|
||||
top: 0,
|
||||
}
|
||||
// detailData.frontBack.back[selectItem.imgDomIndex].style.zIndex = selectItem.printZIndex
|
||||
document.removeEventListener('mousemove',sizeMouseMove)
|
||||
document.removeEventListener('touchmove',sizeTouchmove)
|
||||
document.removeEventListener('mouseup',sizeMouseup)
|
||||
document.removeEventListener('touchend',sizeMouseup)
|
||||
//鼠标抬起
|
||||
setRevocation()
|
||||
}
|
||||
let isMove = false//表示是否移动,是否需要在鼠标抬起的时候保存数据
|
||||
moveableInstance.value.on('scaleStart', ({ target, direction }) => {
|
||||
const frontStyle = detailData.frontBack.front[selectItem.imgDomIndex];
|
||||
if (!frontStyle.mirror){
|
||||
let scaleX = frontStyle.style.transform.match(/scaleX\(([-\d.]+)\)/);
|
||||
let scaleY = frontStyle.style.transform.match(/scaleY\(([-\d.]+)\)/);
|
||||
frontStyle.mirror = { x: scaleX[1] == '-1' ? true : false, y: scaleY[1] == '-1' ? true : false };
|
||||
}
|
||||
});
|
||||
moveableInstance.value.on('rotateStart', ({ target, direction }) => {
|
||||
const frontStyle = detailData.frontBack.front[selectItem.imgDomIndex];
|
||||
if (!frontStyle.mirror){
|
||||
let scaleX = frontStyle.style.transform.match(/scaleX\(([-\d.]+)\)/);
|
||||
let scaleY = frontStyle.style.transform.match(/scaleY\(([-\d.]+)\)/);
|
||||
frontStyle.mirror = { x: scaleX[1] == '-1' ? true : false, y: scaleY[1] == '-1' ? true : false };
|
||||
}
|
||||
});
|
||||
moveableInstance.value.on('dragStart', ({ target, clientX, clientY }) => {
|
||||
startPosition = {
|
||||
left:parseFloat(selectedEl.style.left.replace('px','')) || 0,
|
||||
top:parseFloat(selectedEl.style.top.replace('px','')) || 0,
|
||||
}
|
||||
});
|
||||
// 拖拽
|
||||
moveableInstance.value.on('drag', ({ target, beforeTranslate }) => {
|
||||
let x = startPosition.left + beforeTranslate[0]
|
||||
let y = startPosition.top + beforeTranslate[1]
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].style.left = x + 'px'
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].style.top = y + 'px'
|
||||
});
|
||||
const updateElementTransform = (element, rotateDeg = null) => {
|
||||
|
||||
const currentTransform = element.style?.transform || '';
|
||||
// 1. 提取当前的所有rotate
|
||||
const currentRotates = (currentTransform.match(/rotate[XYZ]?\([^)]+\)/g) || []);
|
||||
// 2. 获取除镜像和rotate外的其他所有变换
|
||||
let otherTransforms = currentTransform
|
||||
.replace(/scaleX\(-1\)|scaleY\(-1\)/g, '') // 移除镜像
|
||||
.replace(/rotate[XYZ]?\([^)]+\)/g, '') // 移除rotate
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
// 3. 构建新transform
|
||||
const transforms = [];
|
||||
// 镜像部分
|
||||
if (element.mirror.x) transforms.push('scaleX(-1)');
|
||||
if (element.mirror.y) transforms.push('scaleY(-1)');
|
||||
if (otherTransforms) transforms.push(otherTransforms);
|
||||
if (rotateDeg !== null) {
|
||||
transforms.push(`rotate(${rotateDeg}deg)`);
|
||||
} else if (currentRotates.length > 0) {
|
||||
transforms.push(...currentRotates);
|
||||
}
|
||||
element.style.transform = transforms.join(' ').trim();
|
||||
};
|
||||
|
||||
moveableInstance.value.on('scale', ({ target, delta, direction }) => {
|
||||
const frontStyle = detailData.frontBack.front[selectItem.imgDomIndex];
|
||||
if (!frontStyle.mirror) frontStyle.mirror = { x: false, y: false };
|
||||
|
||||
const width = parseFloat(frontStyle.style.width);
|
||||
const height = parseFloat(frontStyle.style.height);
|
||||
let left = parseFloat(frontStyle.style.left) || 0;
|
||||
let top = parseFloat(frontStyle.style.top) || 0;
|
||||
let rotation = 0;
|
||||
// 获取原始比例
|
||||
const originalRatio = width / height;
|
||||
if (frontStyle.style.transform) {
|
||||
const transform = frontStyle.style.transform;
|
||||
const match = transform.match(/rotate\(([-\d.]+)deg\)/);
|
||||
if (match) {
|
||||
rotation = parseFloat(match[1]);
|
||||
}
|
||||
}
|
||||
// 根据旋转角度重新计算控制点的方向
|
||||
function getAdjustedCorner(originalCorner, rotationAngle) {
|
||||
const angleRad = rotationAngle * (Math.PI / 180);
|
||||
const cosA = Math.cos(angleRad);
|
||||
const sinA = Math.sin(angleRad);
|
||||
const newX = originalCorner.x * cosA - originalCorner.y * sinA;
|
||||
const newY = originalCorner.x * sinA + originalCorner.y * cosA;
|
||||
const threshold = 0.5;
|
||||
return {
|
||||
x: Math.abs(newX) > threshold ? (newX > 0 ? 1 : -1) : 0,
|
||||
y: Math.abs(newY) > threshold ? (newY > 0 ? 1 : -1) : 0
|
||||
};
|
||||
}
|
||||
|
||||
if (rotation !== 0) {
|
||||
direction = getAdjustedCorner({x: direction[0], y: direction[1]}, rotation);
|
||||
direction = [direction.x, direction.y];
|
||||
}
|
||||
|
||||
// 判断是否是对角线方向(需要等比缩放)
|
||||
const isDiagonal = Math.abs(direction[0]) === 1 && Math.abs(direction[1]) === 1;
|
||||
|
||||
// 处理轴缩放,包含镜像翻转逻辑
|
||||
const processAxis = (axis, val, deltaVal, dir, originalPosition, originalSize, keepRatio = false, otherAxisResult = null) => {
|
||||
let newVal = val * deltaVal;
|
||||
const mirrorKey = axis === 'width' ? 'x' : 'y';
|
||||
const isWidth = axis === 'width';
|
||||
|
||||
// 检查是否需要镜像翻转(当值小于等于0时)
|
||||
if (newVal <= 0) {
|
||||
frontStyle.mirror[mirrorKey] = !frontStyle.mirror[mirrorKey];
|
||||
newVal = Math.abs(newVal);
|
||||
|
||||
updateElementTransform(frontStyle);
|
||||
|
||||
// 镜像翻转后,位置需要根据原始锚点调整
|
||||
if (dir === -1) {
|
||||
// 从左上/右上缩放时,位置保持不变
|
||||
return {
|
||||
newVal,
|
||||
adjustPos: originalPosition,
|
||||
shouldFlip: true
|
||||
};
|
||||
} else {
|
||||
// 从左下/右下缩放时,位置需要调整
|
||||
const newPosition = originalPosition + (originalSize - newVal) * (frontStyle.mirror[mirrorKey] ? -1 : 1);
|
||||
return {
|
||||
newVal,
|
||||
adjustPos: newPosition,
|
||||
shouldFlip: true
|
||||
};
|
||||
}
|
||||
}
|
||||
const shouldMove = (!frontStyle.mirror[mirrorKey] && dir === -1) ||
|
||||
(frontStyle.mirror[mirrorKey] && dir === 1);
|
||||
if (keepRatio && otherAxisResult) {
|
||||
newVal = isWidth ?
|
||||
otherAxisResult.newVal * originalRatio :
|
||||
otherAxisResult.newVal / originalRatio;
|
||||
}
|
||||
let adjustPos;
|
||||
if (shouldMove) {
|
||||
adjustPos = originalPosition - (newVal - originalSize);
|
||||
} else {
|
||||
adjustPos = originalPosition;
|
||||
}
|
||||
return {
|
||||
newVal,
|
||||
adjustPos,
|
||||
shouldFlip: false
|
||||
};
|
||||
};
|
||||
|
||||
// 自由缩放
|
||||
const widthResult = processAxis('width', width, delta[0], direction[0], left, width);
|
||||
const heightResult = processAxis('height', height, delta[1], direction[1], top, height);
|
||||
|
||||
frontStyle.style.left = widthResult.adjustPos + 'px';
|
||||
frontStyle.style.top = heightResult.adjustPos + 'px';
|
||||
frontStyle.style.width = widthResult.newVal + 'px';
|
||||
frontStyle.style.height = heightResult.newVal + 'px';
|
||||
// }
|
||||
});
|
||||
|
||||
// 旋转
|
||||
moveableInstance.value.on('rotate', ({ target, beforeRotate }) => {
|
||||
let frontStyle = detailData.frontBack.front[selectItem.imgDomIndex];
|
||||
// 确保镜像状态存在
|
||||
if (!frontStyle.mirror) frontStyle.mirror = { x: false, y: false };
|
||||
|
||||
const { x: isMirroredX, y: isMirroredY } = frontStyle.mirror;
|
||||
|
||||
// 计算实际旋转角度
|
||||
let actualRotate = beforeRotate;
|
||||
|
||||
// 关键逻辑:当镜像状态不同时(一个true一个false),旋转方向反转
|
||||
if (isMirroredX !== isMirroredY) {
|
||||
actualRotate = -beforeRotate;
|
||||
}
|
||||
|
||||
// 确保角度在 0-360 度范围内
|
||||
actualRotate = actualRotate % 360;
|
||||
|
||||
// 如果角度为负数,转换为正数
|
||||
if (actualRotate < 0) {
|
||||
actualRotate += 360;
|
||||
}
|
||||
|
||||
// 确保角度在 [0, 360) 范围内
|
||||
actualRotate = ((actualRotate % 360) + 360) % 360;
|
||||
|
||||
updateElementTransform(frontStyle, actualRotate.toFixed(2));
|
||||
});
|
||||
// 调整大小
|
||||
moveableInstance.value.on('resize', ({ target, width, height }) => {
|
||||
// console.log(width, height)
|
||||
// detailData.frontBack.front[selectItem.imgDomIndex].style.width = width
|
||||
// detailData.frontBack.front[selectItem.imgDomIndex].style.height = height
|
||||
});
|
||||
moveableInstance.value.on('dragEnd', ({ target, clientX, clientY }) => {
|
||||
startPosition = {
|
||||
left:0,
|
||||
top:0,
|
||||
}
|
||||
upDataDetail()
|
||||
});
|
||||
moveableInstance.value.on('scaleEnd', () => {
|
||||
upDataDetail()
|
||||
});
|
||||
moveableInstance.value.on('rotateEnd', () => {
|
||||
upDataDetail()
|
||||
});
|
||||
};
|
||||
watch(()=>selectItem.selectDetail,(newValue,oldValue)=>{
|
||||
if(!newValue && newValue?.id == oldValue?.id)return
|
||||
selectItem.imgDomIndex = detailData.frontBack.front.findIndex((item:any)=>item.id == newValue.id)
|
||||
initMoveableForSelected()
|
||||
},{immediate: true,})
|
||||
|
||||
const setRevocation = ()=>{
|
||||
if(!isMove)return
|
||||
isMove = false
|
||||
let frontBack = JSON.parse(JSON.stringify(detailData.frontBack))
|
||||
console.log(frontBack)
|
||||
let revocation:any = JSON.parse((sessionStorage.getItem("revocation") as any))
|
||||
revocation.push({designData:null,position:frontBack})
|
||||
sessionStorage.setItem('revocation', JSON.stringify(revocation));
|
||||
|
||||
}
|
||||
const sizeMouseMoveOperation = (e:any)=> {
|
||||
isMove = true
|
||||
let imgDomWH = selectItem.imgDom.getBoundingClientRect()
|
||||
let parentNode =selectItem.imgDom.parentNode
|
||||
let width = imgDomWH.width
|
||||
let height = imgDomWH.height
|
||||
let w,h
|
||||
let num = height/width
|
||||
//判断移动四个边
|
||||
if(selectItem.direction == 'right'){
|
||||
w = (e.clientX - detailData.frontBack.front[selectItem.imgDomIndex].centers.left)
|
||||
h = (e.clientX - detailData.frontBack.front[selectItem.imgDomIndex].centers.left)*num
|
||||
width = w+'px'
|
||||
// height = w*num+'px'
|
||||
}else if(selectItem.direction == 'top'){
|
||||
num = width/height
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].style.top = 'auto'
|
||||
// this.printStyleList[selectItem.imgDomIndex].style.left = 'auto'
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].style.bottom = parentNode.offsetHeight -imgDomWH.height - selectItem.imgDom.offsetTop+'px'
|
||||
w = (e.clientX - detailData.frontBack.front[selectItem.imgDomIndex].centers.left)*num
|
||||
h = (detailData.frontBack.front[selectItem.imgDomIndex].centers.top - e.clientY)
|
||||
|
||||
height = h+'px'
|
||||
// width = h*num+'px'
|
||||
}else if(selectItem.direction == 'bottom'){
|
||||
num = width/height
|
||||
h = (e.clientY - detailData.frontBack.front[selectItem.imgDomIndex].centers.top)
|
||||
height = h+'px'
|
||||
// width = h*num+'px'
|
||||
}else if(selectItem.direction == 'left'){
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].style.left = 'auto'
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].style.right = parentNode.offsetWidth -imgDomWH.width - selectItem.imgDom.offsetLeft+'px'
|
||||
w = (detailData.frontBack.front[selectItem.imgDomIndex].centers.left - e.clientX)
|
||||
|
||||
width = w+'px'
|
||||
// height = w*num+'px'
|
||||
}
|
||||
//判断尺寸是否到边
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].style.width = width
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].style.height = height
|
||||
}
|
||||
// 设置移动
|
||||
const mouseMove = (event:any)=>{
|
||||
let e = getMousePosition(event,false)
|
||||
mouseMoveOperation(e)
|
||||
}
|
||||
const touchmove=(event:any)=>{
|
||||
let e = getMousePosition(event,true)
|
||||
mouseMoveOperation(e)
|
||||
}
|
||||
const mouseup = (e:any)=> {
|
||||
document.removeEventListener('mousemove',mouseMove)
|
||||
document.removeEventListener('touchmove',touchmove)
|
||||
document.removeEventListener('mouseup',mouseup)
|
||||
document.removeEventListener('touchend',mouseup)
|
||||
///鼠标抬起
|
||||
const upDataDetail = async ()=>{
|
||||
//同步到selectDetail数据中,
|
||||
// getDetailListData.designDetail
|
||||
let {scale,offset,priority,transpose,rotate,position,imageSize} = await getSubmitData(selectItem.selectDetail)
|
||||
selectItem.selectDetail.layersObject[0].scale = scale
|
||||
selectItem.selectDetail.layersObject[1].scale = scale
|
||||
selectItem.selectDetail.layersObject[0].offset = offset
|
||||
selectItem.selectDetail.layersObject[1].offset = offset
|
||||
selectItem.selectDetail.layersObject[0].priority = priority
|
||||
selectItem.selectDetail.layersObject[1].priority = priority
|
||||
selectItem.selectDetail.layersObject[0].rotate = rotate
|
||||
selectItem.selectDetail.layersObject[1].rotate = rotate
|
||||
selectItem.selectDetail.layersObject[0].transpose = transpose
|
||||
selectItem.selectDetail.layersObject[1].transpose = transpose
|
||||
selectItem.selectDetail.layersObject[0].position = position
|
||||
selectItem.selectDetail.layersObject[1].position = position
|
||||
selectItem.selectDetail.layersObject[0].imageSize = imageSize
|
||||
selectItem.selectDetail.layersObject[1].imageSize = imageSize
|
||||
setRevocation()
|
||||
}
|
||||
const mouseMoveOperation = (e:any)=>{
|
||||
isMove = true
|
||||
let imgDomWH = selectItem.imgDom.getBoundingClientRect()
|
||||
let parentNode = document.getElementsByClassName('molepositon')[0].getElementsByClassName("designOpenrtion_imgMask")[0].getBoundingClientRect()
|
||||
let x = (e.clientX - detailData.frontBack.front[selectItem.imgDomIndex].centers.left)+'px'
|
||||
let y = ( e.clientY - detailData.frontBack.front[selectItem.imgDomIndex].centers.top)+'px'
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].style.left = x
|
||||
detailData.frontBack.front[selectItem.imgDomIndex].style.top = y
|
||||
}
|
||||
const clothesOpenActive = (index:number)=>{
|
||||
// this.designItemDetail.clothes.forEach((item)=>{
|
||||
// item.clothesOpenItem = false
|
||||
// })
|
||||
// if(index != -1){
|
||||
// this.designItemDetail.clothes[index].clothesOpenItem = true
|
||||
// }
|
||||
}
|
||||
const itemMoveMousedown = async (index:any,e:any)=>{
|
||||
if(detailData.selectDetail.id != detailData.frontBack.front[index].id)return
|
||||
let isModal = false
|
||||
await new Promise((resolve, reject) => {
|
||||
// if(
|
||||
// detailData.isEditPattern.value &&
|
||||
// selectItem.selectDetail?.id &&
|
||||
// (detailData.frontBack.front[index].id != selectItem.selectDetail.id)
|
||||
// ){
|
||||
// isModal = true
|
||||
// Modal.confirm({
|
||||
// title: t('collectionModal.jsContent2'),
|
||||
// icon: createVNode(ExclamationCircleOutlined),
|
||||
// okText: 'Yes',
|
||||
// cancelText: 'No',
|
||||
// mask:false,
|
||||
// centered:true,
|
||||
// onOk() {
|
||||
// resolve(true)
|
||||
// isOpen = true
|
||||
// },
|
||||
// onCancel(){
|
||||
// resolve(false)
|
||||
// isOpen = false
|
||||
// }
|
||||
// });
|
||||
// }else{
|
||||
// if(detailData.frontBack.front[index].id != selectItem.selectDetail.id){
|
||||
// isOpen = true
|
||||
// }
|
||||
// resolve(true)
|
||||
// isModal = false
|
||||
// }
|
||||
resolve(true)
|
||||
}).then((rv)=>{
|
||||
})
|
||||
// emit('canvasReload')
|
||||
// store.commit('DesignDetail/setDesignColthes',detailData.frontBack.front[index].id)
|
||||
if(isModal)return
|
||||
store.commit('DesignDetail/setDesignColthes',detailData.frontBack.front[index].id)
|
||||
selectItem.imgDomIndex = index
|
||||
detailData.frontBack.front.forEach((v:any)=>{
|
||||
v.designOpenrtionBtn = false
|
||||
})
|
||||
clothesOpenActive(index)
|
||||
let event = e||window.event
|
||||
selectItem.imgDom = document.getElementsByClassName('molepositon')[0].getElementsByClassName("detail_modal_item_front")[selectItem.imgDomIndex]
|
||||
detailData.frontBack.front[index].designOpenrtionBtn = true
|
||||
// detailData.frontBack.front[index].style.zIndex = selectItem.printZIndex++
|
||||
// detailData.frontBack.back[index].style.zIndex = selectItem.printZIndex
|
||||
let imgDomWH = selectItem.imgDom.getBoundingClientRect()
|
||||
let left = Number(detailData.frontBack.front[index].style.left.replace(/px/g,''))
|
||||
let top = Number(detailData.frontBack.front[index].style.top.replace(/px/g,''))
|
||||
detailData.frontBack.front[index].centers.left = imgDomWH.x+event.offsetX-left
|
||||
detailData.frontBack.front[index].centers.top = imgDomWH.y+event.offsetY-top
|
||||
|
||||
document.addEventListener('mousemove', mouseMove);
|
||||
document.addEventListener('touchmove', touchmove);
|
||||
document.addEventListener('mouseup', mouseup);
|
||||
document.addEventListener('touchend', mouseup);
|
||||
}
|
||||
const sort = (arr:any)=>{
|
||||
arr.sort((a:any, b:any) => {
|
||||
var a_num = a.style.zIndex;
|
||||
@@ -355,9 +427,9 @@ export default defineComponent({
|
||||
});
|
||||
return arr
|
||||
}
|
||||
const getSubmitData = (value:any,isNoComputed)=>{
|
||||
const getSubmitData = async (value:any)=>{
|
||||
let parentNode = document.getElementsByClassName('molepositon')[0].getElementsByClassName("designOpenrtion_imgMask")[0].getBoundingClientRect()
|
||||
if(!detailData.frontBack?.body?.layersObject?.[0]?.imageSize || isNoComputed){
|
||||
if(!detailData.frontBack?.body?.layersObject?.[0]?.imageSize){
|
||||
return{
|
||||
scale:value.layersObject[0].scale,
|
||||
offset:value.layersObject[0].offset,
|
||||
@@ -365,6 +437,17 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
let ratio = detailData.frontBack.body.layersObject[0].imageSize[0]/parentNode.width
|
||||
|
||||
let scale = 0
|
||||
let dom:any = document.querySelector('.molepositon .perview_img')
|
||||
const img = new Image();
|
||||
img.src = detailData.frontBack?.body?.path;
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
img.onload = () => {
|
||||
scale = dom.parentNode.offsetWidth / img.width
|
||||
resolve()
|
||||
};
|
||||
})
|
||||
// let arr:any = sort(detailData.frontBack.front)
|
||||
let arr:any = sort(JSON.parse(JSON.stringify(detailData.frontBack.front)))
|
||||
let num = 10
|
||||
@@ -377,6 +460,8 @@ export default defineComponent({
|
||||
priority:'',
|
||||
maskUrl:'',
|
||||
maskMinioUrl:'',
|
||||
position:null,
|
||||
imageSize:null,
|
||||
}
|
||||
let state = false
|
||||
for (let index = 0; index < arr.length; index++) {
|
||||
@@ -384,13 +469,27 @@ export default defineComponent({
|
||||
state = true
|
||||
let y = ((arr[index]?.style?.top.replace(/px/g,'')*ratio).toFixed(0) as any - arr[index]?.position[0])
|
||||
let x = ((arr[index]?.style?.left.replace(/px/g,'')*ratio).toFixed(0) as any - arr[index]?.position[1])
|
||||
let positionX = Number((arr[index]?.style?.left.replace(/px/g,'')/scale)).toFixed(2)
|
||||
let positionY = Number((arr[index]?.style?.top.replace(/px/g,'')/scale)).toFixed(2)
|
||||
let imageSizeW = Number((arr[index]?.style?.width.replace(/px/g,'')/scale)).toFixed(2)
|
||||
let imageSizeH = Number((arr[index]?.style?.height.replace(/px/g,'')/scale)).toFixed(2)
|
||||
|
||||
let scaleWidth = arr[index]?.imageSize?Number(((arr[index]?.style?.width.replace(/px/g,'')*ratio)/(arr[index]?.imageSize[0]/arr[index].scale[0])).toFixed(2)):1
|
||||
let scaleHeight = arr[index]?.imageSize?Number(((arr[index]?.style?.height.replace(/px/g,'')*ratio)/(arr[index]?.imageSize[1]/arr[index].scale[1])).toFixed(2)):1
|
||||
// let widthScale = (arr[index].style.width.replace(/px/g,'')/arr[index].style.height.replace(/px/g,'')).toFixed(2)
|
||||
|
||||
let transformStr = arr[index]?.style?.transform
|
||||
let scaleX = transformStr.match(/scaleX\(([-\d.]+)\)/)
|
||||
let scaleY = transformStr.match(/scaleY\(([-\d.]+)\)/)
|
||||
let rotate = transformStr.match(/rotate\(([-\d.]+)deg\)/);
|
||||
data.transpose = [parseFloat(scaleX?.[1] || 1),parseFloat(scaleY?.[1] || 1)]
|
||||
data.rotate = parseFloat(rotate?.[1] || 0)
|
||||
data.scale = [scaleWidth,scaleHeight]
|
||||
let top = y == 0 ? value.layersObject[0].offset[1]:y+value.layersObject[0].offset[1]
|
||||
let left = x == 0 ? value.layersObject[0].offset[0]:x+value.layersObject[0].offset[0]
|
||||
data.offset = [left?left:0,top?top:0]
|
||||
data.position = [positionY,positionX]
|
||||
data.imageSize = [imageSizeW,imageSizeH]
|
||||
// data.offset = [left?left:0,top?top:0]
|
||||
data.maskUrl = arr[index].maskUrl
|
||||
data.maskMinioUrl = arr[index].maskMinioUrl
|
||||
@@ -410,15 +509,6 @@ export default defineComponent({
|
||||
}
|
||||
const deleteNav = ()=>{
|
||||
|
||||
}
|
||||
const setpitch = (item:any,index:any)=>{
|
||||
detailData.frontBack.front.forEach((v:any)=>{
|
||||
v.designOpenrtionBtn = false
|
||||
})
|
||||
detailData.frontBack.front[index].designOpenrtionBtn = true
|
||||
// detailData.frontBack.front[index].style.zIndex = selectItem.printZIndex++
|
||||
// detailData.frontBack.back[index].style.zIndex = selectItem.printZIndex
|
||||
clothesOpenActive(index)
|
||||
}
|
||||
const updataPosition = ()=>{
|
||||
let url = detailData.frontBack?.body?.path
|
||||
@@ -431,10 +521,12 @@ export default defineComponent({
|
||||
detailData.frontBack.front.forEach((item:any,index:number) => {
|
||||
for (const key in item.style) {
|
||||
if(key == 'zIndex')return
|
||||
if(key == 'transform')return
|
||||
item.style[key] = item.style[key]*sacle+'px'
|
||||
}
|
||||
for (const key in detailData.frontBack.back[index].style) {
|
||||
if(key == 'zIndex')return
|
||||
if(key == 'transform')return
|
||||
detailData.frontBack.back[index].style[key] = detailData.frontBack.back[index].style[key]*sacle+'px'
|
||||
}
|
||||
});
|
||||
@@ -445,20 +537,22 @@ export default defineComponent({
|
||||
if (detailData.observer) {
|
||||
detailData.observer.disconnect()
|
||||
}
|
||||
if (moveableInstance.value) {
|
||||
moveableInstance.value.destroy();
|
||||
}
|
||||
})
|
||||
return{
|
||||
...toRefs(detailData),
|
||||
...toRefs(selectItem),
|
||||
...toRefs(getDetailListDom),
|
||||
setElementRef,
|
||||
|
||||
setPrintSize,
|
||||
itemSizeMousedown,
|
||||
itemMoveMousedown,
|
||||
deleteNav,
|
||||
setpitch,
|
||||
getSubmitData,
|
||||
getMousePosition,
|
||||
updataPosition,
|
||||
updateRect,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -541,6 +635,17 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
.moveableContainer{
|
||||
:deep(.moveable-origin){
|
||||
opacity: 0;
|
||||
}
|
||||
:deep(.moveable-control){
|
||||
border-radius: 0;
|
||||
}
|
||||
:deep(.moveable-rotation-control){
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
> .designOpenrtion_imgMask{
|
||||
width: auto;
|
||||
height: auto;
|
||||
@@ -561,10 +666,15 @@ export default defineComponent({
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.detail_modal_item_front,.designOpenrtion_print{
|
||||
z-index: 2;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
&.active{
|
||||
pointer-events: auto;
|
||||
}
|
||||
img{
|
||||
width: 100%;
|
||||
// height: ;
|
||||
@@ -583,94 +693,6 @@ export default defineComponent({
|
||||
.designOpenrtion_print{
|
||||
z-index: 1 !important;
|
||||
}
|
||||
> .designOpenrtion_btnBox{
|
||||
z-index: 99;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
ul{
|
||||
list-style: none;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid rgb(20, 188, 255);
|
||||
padding: 0;
|
||||
-webkit-user-drag: none;
|
||||
user-select:none;
|
||||
opacity: 0;
|
||||
margin: 0;
|
||||
|
||||
li{
|
||||
cursor: pointer;
|
||||
// border-radius: 50%;
|
||||
width: calc(2rem*1.2);
|
||||
height: calc(2rem*1.2);
|
||||
background-color: rgb(20, 188, 255);
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
&.active{
|
||||
opacity: 1;
|
||||
z-index: 999 !important;
|
||||
li{
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
.designOpenrtion_btn_top,.designOpenrtion_btn_bottom{
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%) ;
|
||||
cursor: n-resize;
|
||||
}
|
||||
.designOpenrtion_btn_top{
|
||||
top: 0;
|
||||
}
|
||||
.designOpenrtion_btn_bottom{
|
||||
top: 100%;
|
||||
}
|
||||
.designOpenrtion_btn_left,.designOpenrtion_btn_right{
|
||||
top: 50%;
|
||||
transform: translate(-50%,-50%) ;
|
||||
cursor: e-resize;
|
||||
}
|
||||
.designOpenrtion_btn_left{
|
||||
left: 0;
|
||||
}
|
||||
.designOpenrtion_btn_right{
|
||||
left: 100%;
|
||||
}
|
||||
.designOpenrtion_rotote{
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.designOpenrtion_rotote::after{
|
||||
position: absolute;
|
||||
content: "";
|
||||
background-color: #14bcff;
|
||||
width: 2px;
|
||||
height: 30px;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.designOpenrtion_rotote::before{
|
||||
position: absolute;
|
||||
content: "";
|
||||
background-color: #14bcff;
|
||||
top: calc(50% - 30px);
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%) ;
|
||||
width: calc(1.5rem*1.2);
|
||||
height: calc(1.5rem*1.2);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1074,10 +1074,10 @@ export default defineComponent({
|
||||
const scrollTop = textarea.scrollTop
|
||||
|
||||
// 2. 计算单行高度
|
||||
const lineHeight = parseInt(getComputedStyle(textarea).lineHeight) || 20 // 默认20px
|
||||
const lineHeight:any = parseInt(getComputedStyle(textarea).lineHeight) || 20 // 默认20px
|
||||
|
||||
// 3. 重置高度为1行
|
||||
textarea.style.height = lineHeight + 'px'
|
||||
textarea.style.height = (parseInt(lineHeight)+4) + 'px'
|
||||
|
||||
// 4. 计算实际需要的高度
|
||||
const newHeight = Math.max(lineHeight, textarea.scrollHeight)
|
||||
|
||||
@@ -476,9 +476,9 @@ export default defineComponent({
|
||||
if(!textarea)return
|
||||
const scrollTop = textarea.scrollTop;
|
||||
// 2. 计算单行高度
|
||||
const lineHeight = parseInt(getComputedStyle(textarea).lineHeight) || 20; // 默认20px
|
||||
const lineHeight:any = parseInt(getComputedStyle(textarea).lineHeight) || 20; // 默认20px
|
||||
// 3. 重置高度为1行
|
||||
textarea.style.height = lineHeight + 'px';
|
||||
textarea.style.height = (parseInt(lineHeight)+4) + 'px'
|
||||
// 4. 计算实际需要的高度
|
||||
const newHeight = Math.max(lineHeight, textarea.scrollHeight);
|
||||
textarea.style.height = newHeight + 'px';
|
||||
|
||||
@@ -132,7 +132,25 @@ export default defineComponent({
|
||||
url:rv.url,
|
||||
designType:props.item.resData.designType || props.item.designType,
|
||||
}
|
||||
props.list.unshift(rv)
|
||||
rv.type_ = {
|
||||
type1: "material",
|
||||
type2: props.level1Type
|
||||
}
|
||||
if(props.level1Type == 'Printboard'){
|
||||
let list = props.list.filter((v:any)=> v.type_?.type1 == "material")
|
||||
list.unshift(rv)
|
||||
store.commit("setPrintboardMaterialFiles", list);
|
||||
}else if(props.level1Type == 'Sketchboard'){
|
||||
let list = props.list.filter((v:any)=> v.type_?.type1 == "material")
|
||||
list.unshift(rv)
|
||||
store.commit("setSketchboardMaterialFiles", list);
|
||||
}else if(props.level1Type == 'Moodleboard'){
|
||||
let list = props.list.filter((v:any)=> v.type_?.type1 == "material")
|
||||
list.unshift(rv)
|
||||
store.commit("setMoodleboardMaterialFiles", list);
|
||||
}else{
|
||||
props.list.unshift(rv)
|
||||
}
|
||||
}
|
||||
}
|
||||
).catch(res=>{
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
:isBackgroundChangeable="false"
|
||||
ref="editCanvas"></editCanvas>
|
||||
</div>
|
||||
<div class="btn">
|
||||
<div class="btn" v-if="btnShow">
|
||||
<div class="gallery_btn" @click="canvasSave" style="width: min-content;margin-top: auto;">{{ $t('exportModel.Save') }}</div>
|
||||
<div class="gallery_btn" @click="exportElement">{{ $t('exportModel.Export') }}</div>
|
||||
</div>
|
||||
@@ -46,7 +46,11 @@ export default defineComponent({
|
||||
isSubmitCanvasJSON:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
}
|
||||
},
|
||||
btnShow:{
|
||||
type:Boolean,
|
||||
default:true
|
||||
},
|
||||
},
|
||||
emits:['submitBase64Data','canvasChangeGetJSON'],
|
||||
setup(props,{emit}) {
|
||||
@@ -99,6 +103,9 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
}
|
||||
const submitBase64Data = (base64Data)=>{
|
||||
return dataDom.editCanvas.exportImage({isContainBg:true,isContainFixed:true,isCropByBg:true})
|
||||
}
|
||||
const exportElement = ()=>{
|
||||
dataDom.editCanvas.exportImage({isContainBg:true,isContainFixed:false,isCropByBg:true}).then((rv)=>{
|
||||
downloadBase64Image(rv,'canvas')
|
||||
@@ -174,6 +181,7 @@ export default defineComponent({
|
||||
canvasInit,
|
||||
exportElement,
|
||||
changeCanvas,
|
||||
submitBase64Data,
|
||||
};
|
||||
},
|
||||
data(prop) {
|
||||
|
||||
@@ -1497,6 +1497,7 @@ export default {
|
||||
CompositeColorTip: '颜色:保留原图像饱和度,改变新图像颜色',
|
||||
CompositeLuminosity: '亮度',
|
||||
CompositeLuminosityTip: '亮度:保留原图像颜色,改变新图像亮度',
|
||||
GarmentPartSelector: '服装部件选取',
|
||||
},
|
||||
speedList: {
|
||||
High: '高级',
|
||||
|
||||
@@ -1547,7 +1547,8 @@ export default {
|
||||
'Color: Preserve the original image saturation and change the color of the new image',
|
||||
CompositeLuminosity: 'Luminosity',
|
||||
CompositeLuminosityTip:
|
||||
'Luminosity: Preserve the original image color and change the luminosity of the new image'
|
||||
'Luminosity: Preserve the original image color and change the luminosity of the new image',
|
||||
GarmentPartSelector: 'Garment Part Selector',
|
||||
},
|
||||
speedList: {
|
||||
High: 'High',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {Module} from 'vuex'
|
||||
import {RootState} from '../index'
|
||||
import { forEach } from 'jszip'
|
||||
import { transform } from 'typescript'
|
||||
|
||||
interface DesignDetail{
|
||||
designDetail:any,
|
||||
@@ -53,12 +54,32 @@ const DesignDetail : Module<DesignDetail,RootState> = {
|
||||
data.clothes.forEach((v:any,index:any)=>{
|
||||
// if(!v?.partialDesignDTO){
|
||||
// }
|
||||
function isJSONString(str) {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
return true; // 解析成功,是有效的JSON字符串
|
||||
} catch (e) {
|
||||
return false; // 解析失败,不是有效的JSON字符串
|
||||
}
|
||||
}
|
||||
v?.printObject?.prints?.forEach((item:any) => {
|
||||
console.log(item.object)
|
||||
if(isJSONString(item.object)){
|
||||
item.object = JSON.parse(item.object)
|
||||
}
|
||||
});
|
||||
v?.trims?.prints?.forEach((item:any) => {
|
||||
if(isJSONString(item.object)){
|
||||
item.object = JSON.parse(item.object)
|
||||
}
|
||||
});
|
||||
for (let i = v.layersObject.length-1; i >= 0; i--) {
|
||||
v.layersObject[i].style = {
|
||||
top:v.layersObject[i].position?.[0],
|
||||
left:v.layersObject[i].position?.[1],
|
||||
width:v.layersObject[i].imageSize?.[0],
|
||||
height:v.layersObject[i].imageSize?.[1],
|
||||
transform:`rotate(${v.layersObject[i]?.rotate || 0}deg) scaleX(${v.layersObject[i].transpose?.[0] || 1}) scaleY(${v.layersObject[i].transpose?.[1] || 1})`,
|
||||
}
|
||||
v.layersObject[i].centers={
|
||||
left:0,
|
||||
@@ -69,16 +90,12 @@ const DesignDetail : Module<DesignDetail,RootState> = {
|
||||
v.layersObject[i].designOpenrtionBtn = false
|
||||
if(v.layersObject[i].imageCategory.indexOf("back") == -1){
|
||||
front[index] = v.layersObject[i]
|
||||
// front[index].style.zIndex = v.priority
|
||||
front[index].style.zIndex = v.priority + 10
|
||||
front[index].id = v.id
|
||||
front[index].undividedLayer = v.undividedLayer
|
||||
front[index].undividedLayerWithSinglePrint = v?.undividedLayerWithSinglePrint
|
||||
}else{
|
||||
back[index] = v.layersObject[i]
|
||||
back[index].style.zIndex = v.priority
|
||||
back[index].id = v.id
|
||||
back[index].undividedLayer = v.undividedLayer
|
||||
back[index].undividedLayerWithSinglePrint = v?.undividedLayerWithSinglePrint
|
||||
// back[index].style.zIndex = backIndex==0?v.layersObject[i]:backIndex++
|
||||
}
|
||||
if(state.printZIndex < v.priority){
|
||||
@@ -198,8 +215,8 @@ const DesignDetail : Module<DesignDetail,RootState> = {
|
||||
printObject:null,
|
||||
trims:null,
|
||||
type:null,
|
||||
undividedLayer:null,
|
||||
undividedLayerWithSinglePrint:null,
|
||||
transpose:[1,1],
|
||||
rotate:0,
|
||||
}
|
||||
if(!state.currentDetailType)state.currentDetailType = 'sketch'
|
||||
state.designDetail.clothes.forEach((item:any) => {
|
||||
@@ -209,6 +226,28 @@ const DesignDetail : Module<DesignDetail,RootState> = {
|
||||
});
|
||||
state.selectDetail = data
|
||||
},
|
||||
canvasPreviewUpdata(state,{type,callBack}){
|
||||
console.log(state.selectDetail,type)
|
||||
// state.selectDetail.newDetail?.print?.forEach((item:any) => {
|
||||
// state.selectDetail.printObject.prints = []
|
||||
// state.selectDetail.printObject.push({
|
||||
// })
|
||||
// });
|
||||
if(type == 'print' || type == 'all')state.selectDetail.printObject.prints = state.selectDetail.newDetail?.print
|
||||
if(type == 'color' || type == 'all')state.selectDetail.color = {
|
||||
...state.selectDetail.newDetail?.color,
|
||||
...state.selectDetail.newDetail?.color?.rgba,
|
||||
...state.selectDetail.newDetail?.color?.hsv,
|
||||
}
|
||||
console.log(state.selectDetail,type,state.selectDetail.newDetail)
|
||||
if(type == 'element' || type == 'all')state.selectDetail.trims.prints = state.selectDetail.newDetail?.element
|
||||
if(type == 'all'){
|
||||
state.selectDetail.newDetail = {}
|
||||
}else{
|
||||
state.selectDetail.newDetail[type] = null
|
||||
}
|
||||
callBack()
|
||||
},
|
||||
async setPraeview(state,value){//preview
|
||||
let data = value?.rv || value
|
||||
let currentType = value?.currentType
|
||||
@@ -228,12 +267,9 @@ const DesignDetail : Module<DesignDetail,RootState> = {
|
||||
}else{
|
||||
id_ = item.id
|
||||
}
|
||||
console.log(id_)
|
||||
let el:any = document.querySelector('.molepositon .perview_img')
|
||||
await new Promise((resolve, reject) => {
|
||||
if(!state.frontBack?.body?.path){
|
||||
state.frontBack.front[0].undividedLayer = value.rv.clothes[0].undividedLayer
|
||||
state.frontBack.front[0].undividedLayerWithSinglePrint = value.rv.clothes[0]?.undividedLayerWithSinglePrint
|
||||
resolve('')
|
||||
}
|
||||
const img = new Image();
|
||||
@@ -252,7 +288,8 @@ const DesignDetail : Module<DesignDetail,RootState> = {
|
||||
left:item.layersObject[i].position?.[1] * scale + 'px',
|
||||
width:item.layersObject[i].imageSize?.[0] * scale + 'px',
|
||||
height:item.layersObject[i].imageSize?.[1] * scale + 'px',
|
||||
zIndex:v?.style?.zIndex?v.style.zIndex:v.priority?v.priority:state.frontBack.front.length
|
||||
zIndex:v?.style?.zIndex?v.style.zIndex:v.priority?v.priority:state.frontBack.front.length,
|
||||
transform:`rotate(${item.layersObject?.[i]?.rotate || 0}deg) scaleX(${item.layersObject[i].transpose?.[0] || 1}) scaleY(${item.layersObject[i].transpose?.[1] || 1})`,
|
||||
}
|
||||
item.layersObject[i].centers={
|
||||
left:0,
|
||||
@@ -263,14 +300,10 @@ const DesignDetail : Module<DesignDetail,RootState> = {
|
||||
state.frontBack.front[index] = item.layersObject[i]
|
||||
// state.frontBack.front[index].style.zIndex = v.priority
|
||||
state.frontBack.front[index].id = item.id
|
||||
state.frontBack.front[index].undividedLayer = item.undividedLayer
|
||||
state.frontBack.front[index].undividedLayerWithSinglePrint = item?.undividedLayerWithSinglePrint
|
||||
}else{
|
||||
state.frontBack.back[index] = item.layersObject[i]
|
||||
// state.frontBack.back[index].style.zIndex = v.priority
|
||||
state.frontBack.back[index].id = item.id
|
||||
state.frontBack.back[index].undividedLayer = item.undividedLayer
|
||||
state.frontBack.back[index].undividedLayerWithSinglePrint = item?.undividedLayerWithSinglePrint
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,6 +326,8 @@ const DesignDetail : Module<DesignDetail,RootState> = {
|
||||
detailItem.minIOPath = item.minIOPath
|
||||
detailItem.scale = [1,1]
|
||||
detailItem.offset = [0,0]
|
||||
detailItem.transpose = item.transpose || [1,1]
|
||||
detailItem.rotate = item.rotate || 0
|
||||
detailItem.printObject = item.printObject
|
||||
detailItem.trims = item.trims
|
||||
detailItem.type = item.type
|
||||
@@ -307,8 +342,6 @@ const DesignDetail : Module<DesignDetail,RootState> = {
|
||||
},
|
||||
gradient:item.gradient,
|
||||
}
|
||||
detailItem.undividedLayer = item.undividedLayer
|
||||
detailItem.undividedLayerWithSinglePrint = item?.undividedLayerWithSinglePrint
|
||||
if(detailItem.newDetail?.sketch?.id && !detailItem.id){
|
||||
state.designDetail.clothes.push(detailItem)
|
||||
}
|
||||
@@ -354,8 +387,6 @@ const DesignDetail : Module<DesignDetail,RootState> = {
|
||||
// },
|
||||
// gradient:item.gradient,
|
||||
// }
|
||||
// state.selectDetail.undividedLayer = item.undividedLayer
|
||||
// state.selectDetail.undividedLayerWithSinglePrint = item?.undividedLayerWithSinglePrint
|
||||
// if(state.selectDetail.newDetail?.sketch?.id && !state.selectDetail.id){
|
||||
// state.designDetail.clothes.push(state.selectDetail)
|
||||
// }
|
||||
|
||||
@@ -440,7 +440,10 @@ export const Https = {
|
||||
getHistoryNotification: `/api/message/getHistoryNotification`, //获取历史消息
|
||||
oneClickRead: `/api/message/oneClickRead`, //全部设为已读
|
||||
personalHomepage: `/api/account/personalHomepage`, //获取个人主页信息
|
||||
refreshMinioUrl: `/api/third/party/refreshMinioUrl` //获取可以使用的minio地址
|
||||
refreshMinioUrl: `/api/third/party/refreshMinioUrl`, //获取可以使用的minio地址
|
||||
|
||||
// 画布
|
||||
segAnything: `/api/python/segAnything`,//分割Anything
|
||||
},
|
||||
|
||||
axiosGet(url, config) {
|
||||
|
||||
@@ -618,6 +618,60 @@ function segmentImage(markerImage,fullImage,size){
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理PNG图片:透明度转白色,其他颜色转透明
|
||||
* @param {string} sketchImage - 原始图片
|
||||
* @returns {Promise} 处理后的ase64
|
||||
*/
|
||||
function sketchToMask(sketchImage) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.onload = function() {
|
||||
try {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
ctx.drawImage(img, 0, 0);
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const data = imageData.data;
|
||||
|
||||
for (let i = 0; i < data.length; i += 4) {
|
||||
const r = data[i];
|
||||
const g = data[i + 1];
|
||||
const b = data[i + 2];
|
||||
const a = data[i + 3];
|
||||
if (a > 0) {
|
||||
data[i] = 0;
|
||||
data[i + 1] = 0;
|
||||
data[i + 2] = 0;
|
||||
data[i + 3] = 0;
|
||||
} else {
|
||||
// 完全透明的像素 -> 纯白色
|
||||
data[i] = 255;
|
||||
data[i + 1] = 255;
|
||||
data[i + 2] = 255;
|
||||
data[i + 3] = 255;
|
||||
}
|
||||
}
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
const base64 = canvas.toDataURL('image/png');
|
||||
resolve(base64);
|
||||
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
img.onerror = function() {
|
||||
reject(new Error('图片加载失败'));
|
||||
};
|
||||
|
||||
img.src = sketchImage;
|
||||
});
|
||||
}
|
||||
export {
|
||||
isEmail,
|
||||
getUploadUrl,
|
||||
@@ -640,5 +694,6 @@ export {
|
||||
setGradual,
|
||||
calculateGradientCoordinate,
|
||||
segmentImage,
|
||||
UrlToFile
|
||||
UrlToFile,
|
||||
sketchToMask
|
||||
}
|
||||