Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/FiDA_Front
173
package-lock.json
generated
@@ -17,6 +17,7 @@
|
||||
"element-plus": "^2.13.2",
|
||||
"fabric-with-all": "^5.3.1",
|
||||
"gsap": "^3.13.0",
|
||||
"jszip": "^3.10.1",
|
||||
"md5": "^2.3.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"pinia": "^2.0.32",
|
||||
@@ -2435,6 +2436,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz",
|
||||
@@ -4812,6 +4819,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
@@ -4856,8 +4869,7 @@
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"devOptional": true
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/internal-slot": {
|
||||
"version": "1.1.0",
|
||||
@@ -5385,8 +5397,7 @@
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
@@ -5519,6 +5530,48 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"license": "(MIT OR GPL-3.0-or-later)",
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip/node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jszip/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/kind-of": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-5.1.0.tgz",
|
||||
@@ -5573,6 +5626,15 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lilconfig": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||
@@ -7408,6 +7470,12 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -7790,6 +7858,12 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/property-information": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
|
||||
@@ -8446,6 +8520,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -10230,8 +10310,7 @@
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"devOptional": true
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"node_modules/validate-npm-package-license": {
|
||||
"version": "3.0.4",
|
||||
@@ -12506,6 +12585,11 @@
|
||||
"integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||
},
|
||||
"cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz",
|
||||
@@ -14269,6 +14353,11 @@
|
||||
"integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
|
||||
"dev": true
|
||||
},
|
||||
"immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
@@ -14304,8 +14393,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"devOptional": true
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"internal-slot": {
|
||||
"version": "1.1.0",
|
||||
@@ -14660,8 +14748,7 @@
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
|
||||
},
|
||||
"isexe": {
|
||||
"version": "2.0.0",
|
||||
@@ -14771,6 +14858,46 @@
|
||||
"universalify": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"requires": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-5.1.0.tgz",
|
||||
@@ -14813,6 +14940,14 @@
|
||||
"type-check": "~0.4.0"
|
||||
}
|
||||
},
|
||||
"lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"requires": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"lilconfig": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||
@@ -16078,6 +16213,11 @@
|
||||
"aggregate-error": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||
},
|
||||
"parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -16341,6 +16481,11 @@
|
||||
"fast-diff": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"property-information": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
|
||||
@@ -16833,6 +16978,11 @@
|
||||
"split-string": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -18176,8 +18326,7 @@
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"devOptional": true
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"validate-npm-package-license": {
|
||||
"version": "3.0.4",
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"element-plus": "^2.13.2",
|
||||
"fabric-with-all": "^5.3.1",
|
||||
"gsap": "^3.13.0",
|
||||
"jszip": "^3.10.1",
|
||||
"md5": "^2.3.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"pinia": "^2.0.32",
|
||||
|
||||
@@ -9,10 +9,11 @@ import request from '@/utils/request'
|
||||
export interface getSketchFlowCanvasData {
|
||||
id: string
|
||||
}
|
||||
export const getSketchFlowCanvas = (data:getSketchFlowCanvasData) => {
|
||||
export const getSketchFlowCanvas = (data:getSketchFlowCanvasData,loading?:boolean) => {
|
||||
return request({
|
||||
url: `/api/canvas/detail/${data.id}`,
|
||||
method: 'get',
|
||||
loading
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,14 +28,15 @@ export interface saveSketchFlowCanvasData {
|
||||
id?: string
|
||||
canvasData: string
|
||||
}
|
||||
export const putSketchFlowCanvas = (data:saveSketchFlowCanvasData) => {
|
||||
export const putSketchFlowCanvas = (data:saveSketchFlowCanvasData,loading?:boolean) => {
|
||||
return request({
|
||||
url: `/api/canvas/detail/${data.id}`,
|
||||
method: 'put',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: data.canvasData
|
||||
data: data.canvasData,
|
||||
loading
|
||||
})
|
||||
}
|
||||
|
||||
@@ -112,6 +114,7 @@ export const toRealStyleApi = (data:toRealStyleData) => {
|
||||
* @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以
|
||||
* @param data.variantCount 生成图片的数量
|
||||
* @param data.colors 生成上色的图片颜色列表
|
||||
* @param data.mode 选择的模型
|
||||
* @returns 线稿图上色
|
||||
*/
|
||||
export interface toColorPaletteData {
|
||||
@@ -119,6 +122,7 @@ export interface toColorPaletteData {
|
||||
imageUrl?: string
|
||||
variantCount?: string
|
||||
colors?: Array<string>
|
||||
mode?: string
|
||||
}
|
||||
export const toColorPaletteApi = (data:toColorPaletteData) => {
|
||||
return request({
|
||||
@@ -128,7 +132,8 @@ export const toColorPaletteApi = (data:toColorPaletteData) => {
|
||||
sketchId: data.sketchId,
|
||||
imageUrl: data.imageUrl,
|
||||
variantCount: data.variantCount,
|
||||
colors: data.colors
|
||||
colors: data.colors,
|
||||
mode: data.mode,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -140,6 +145,7 @@ export const toColorPaletteApi = (data:toColorPaletteData) => {
|
||||
* @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以
|
||||
* @param data.styles 生成上色的图片颜色列表
|
||||
* @param data.userPrompt 生成上色的图片颜色列表
|
||||
* @param data.mode 选择的模型
|
||||
* @returns 场景构图
|
||||
*/
|
||||
export interface toSceneCompositionData {
|
||||
@@ -147,6 +153,7 @@ export interface toSceneCompositionData {
|
||||
imageUrl?: string
|
||||
userPrompt?: string
|
||||
styles?: Array<string>
|
||||
mode?: string
|
||||
}
|
||||
export const toSceneCompositionApi = (data:toSceneCompositionData) => {
|
||||
return request({
|
||||
@@ -156,8 +163,83 @@ export const toSceneCompositionApi = (data:toSceneCompositionData) => {
|
||||
sketchId: data.sketchId,
|
||||
imageUrl: data.imageUrl,
|
||||
userPrompt: data.userPrompt,
|
||||
styles: data.styles
|
||||
styles: data.styles,
|
||||
mode: data.mode,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 线稿贴印花
|
||||
* @param data 线稿贴印花的参数
|
||||
* @param data.sketchId sketch id
|
||||
* @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以
|
||||
* @param data.styles 生成上色的图片颜色列表
|
||||
* @param data.userPrompt 生成上色的图片颜色列表
|
||||
* @param data.mode 选择的模型
|
||||
* @returns 线稿贴印花
|
||||
*/
|
||||
export interface sketchAddPrintData {
|
||||
sketchId?: string
|
||||
imageUrl?: string
|
||||
surFaceUrl?: string
|
||||
userPrompt?: string
|
||||
mode?: string
|
||||
}
|
||||
export const sketchAddPrintApi = (data:sketchAddPrintData) => {
|
||||
return request({
|
||||
url: `/api/image/surface-edit`,
|
||||
method: 'post',
|
||||
data:{
|
||||
sketchId: data.sketchId,
|
||||
imageUrl: data.imageUrl,
|
||||
userPrompt: data.userPrompt,
|
||||
surFaceUrl: data.surFaceUrl,
|
||||
mode: data.mode,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片转3d
|
||||
* @param data 图片转3d的参数
|
||||
* @param data.sketchId sketch id
|
||||
* @param data.imageUrls 进行生成的图片。minio地址和正常地址都可以
|
||||
* @returns 图片转3d
|
||||
*/
|
||||
export interface sketchToThreeData {
|
||||
sketchId?: string
|
||||
imageUrls?: Array<string>
|
||||
}
|
||||
export const sketchToThreeApi = (data:sketchToThreeData) => {
|
||||
return request({
|
||||
url: `/api/image/img-to-3d`,
|
||||
method: 'post',
|
||||
data:{
|
||||
sketchId: data.sketchId,
|
||||
imageUrls: data.imageUrls,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 3d模型转3视图
|
||||
* @param data 3d模型转3视图的参数
|
||||
* @param data.sketchId sketch id
|
||||
* @param data.glbUrl 生成的3d模型地址
|
||||
* @returns 3d模型转3视图
|
||||
*/
|
||||
export interface threeToThreeViewsData {
|
||||
sketchId?: string
|
||||
glbUrl?: string
|
||||
}
|
||||
export const threeToThreeViewsApi = (data:threeToThreeViewsData) => {
|
||||
return request({
|
||||
url: `/api/image/glb-to-3views`,
|
||||
method: 'post',
|
||||
data:{
|
||||
sketchId: data.sketchId,
|
||||
glbUrl: data.glbUrl,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export const uploadImage = (data: FormData) => {
|
||||
export const uploadImage = (data: FormData, loading = false) => {
|
||||
return request({
|
||||
url: '/api/file/upload',
|
||||
method: 'POST',
|
||||
loading,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -109,3 +109,27 @@ export const UpdateUserProfile = (data, loading = false) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传头像
|
||||
* @param data 上传头像的参数
|
||||
* @param data.avatar 头像文件
|
||||
* @returns 上传头像成功的响应
|
||||
*/
|
||||
export const UpdateUserAvatar = (data) => {
|
||||
return request({
|
||||
url: '/api/user/avatar',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传头像次数
|
||||
*/
|
||||
export const getAvatarLimit = () => {
|
||||
return request({
|
||||
url: '/api/user/avatar/limit',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
3
src/assets/icons/dc/arrow.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.085 5.92551H0.54125C0.387639 5.92551 0.259028 5.87405 0.155417 5.77113C0.0518057 5.66835 0 5.54071 0 5.38821C0 5.23571 0.0518057 5.10662 0.155417 5.00092C0.259028 4.89509 0.387639 4.84217 0.54125 4.84217H9.085L5.18958 0.946756C5.0825 0.839673 5.02625 0.713491 5.02083 0.568214C5.01556 0.422936 5.07056 0.289255 5.18583 0.167172C5.30125 0.0531442 5.43028 -0.00254935 5.57292 8.95351e-05C5.71556 0.00272842 5.84319 0.0605064 5.95583 0.173423L10.6935 4.91717C10.7633 4.98703 10.8141 5.06044 10.846 5.13738C10.8781 5.21433 10.8942 5.29759 10.8942 5.38717C10.8942 5.47662 10.8781 5.55988 10.846 5.63696C10.8141 5.71391 10.766 5.78446 10.7019 5.84863L5.96 10.5905C5.84458 10.7059 5.71903 10.7615 5.58333 10.7572C5.44764 10.7529 5.32208 10.6937 5.20667 10.5797C5.09139 10.4576 5.03375 10.3254 5.03375 10.1832C5.03375 10.041 5.09139 9.91446 5.20667 9.80363L9.085 5.92551Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 995 B |
4
src/assets/icons/dc/copy.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.725098" y="0.724609" width="13.0481" height="13.0481" rx="2.17468" stroke="black" stroke-width="1.44978"/>
|
||||
<path d="M16.6733 5.79687V12.3209C16.6733 14.723 14.726 16.6703 12.3239 16.6703H4.3501" stroke="black" stroke-width="1.44978" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 372 B |
3
src/assets/icons/dc/dui.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="10" height="7" viewBox="0 0 10 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.84125 5.40854L8.06875 0.172916C8.18375 0.0576386 8.31264 0 8.45542 0C8.59833 0 8.7275 0.0575006 8.84292 0.172501C8.9582 0.287501 9.01583 0.416458 9.01583 0.559375C9.01583 0.70243 8.9582 0.831667 8.84292 0.947084L3.30604 6.47583C3.16924 6.61264 3.01083 6.68104 2.83083 6.68104C2.65083 6.68104 2.49243 6.61264 2.35563 6.47583L0.172917 4.30125C0.0576393 4.18625 0 4.05736 0 3.91458C0 3.77167 0.0575 3.6425 0.1725 3.52708C0.2875 3.41181 0.416458 3.35417 0.559375 3.35417C0.702431 3.35417 0.831667 3.41181 0.947083 3.52708L2.84125 5.40854Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 664 B |
3
src/assets/icons/dc/ellipse.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.59042 15.1667C6.54722 15.1667 5.56472 14.9693 4.64292 14.5746C3.72111 14.1799 2.91431 13.6367 2.2225 12.9452C1.53069 12.2537 0.987292 11.4475 0.592292 10.5267C0.19743 9.60597 0 8.62215 0 7.57521C0 6.52826 0.197361 5.54736 0.592083 4.6325C0.986806 3.71764 1.52993 2.91431 2.22146 2.2225C2.91299 1.53069 3.71917 0.987292 4.64 0.592292C5.56069 0.19743 6.54451 0 7.59146 0C8.6384 0 9.61931 0.197361 10.5342 0.592083C11.449 0.986805 12.2524 1.52993 12.9442 2.22146C13.636 2.91299 14.1794 3.7175 14.5744 4.635C14.9692 5.55264 15.1667 6.53306 15.1667 7.57625C15.1667 8.61944 14.9693 9.60194 14.5746 10.5238C14.1799 11.4456 13.6367 12.2524 12.9452 12.9442C12.2537 13.636 11.4492 14.1794 10.5317 14.5744C9.61403 14.9692 8.63361 15.1667 7.59042 15.1667ZM7.58333 14.0833C9.38889 14.0833 10.9236 13.4514 12.1875 12.1875C13.4514 10.9236 14.0833 9.38889 14.0833 7.58333C14.0833 5.77778 13.4514 4.24306 12.1875 2.97917C10.9236 1.71528 9.38889 1.08333 7.58333 1.08333C5.77778 1.08333 4.24306 1.71528 2.97917 2.97917C1.71528 4.24306 1.08333 5.77778 1.08333 7.58333C1.08333 9.38889 1.71528 10.9236 2.97917 12.1875C4.24306 13.4514 5.77778 14.0833 7.58333 14.0833Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
3
src/assets/icons/dc/line.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.197083 9.55862C0.0656943 9.42723 0 9.28244 0 9.12424C0 8.96619 0.0656943 8.82251 0.197083 8.6932L8.70521 0.193203C8.8366 0.0670915 8.97944 0.0027174 9.13375 7.85068e-05C9.28819 -0.00256038 9.43062 0.0613275 9.56104 0.191744C9.6934 0.324105 9.75958 0.469384 9.75958 0.627578C9.75958 0.785634 9.69389 0.929315 9.5625 1.05862L1.0625 9.55862C0.933194 9.69001 0.789514 9.7557 0.631458 9.7557C0.473264 9.7557 0.328472 9.69001 0.197083 9.55862Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 569 B |
1
src/assets/icons/dc/lock_0.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747121366655" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="56887" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M800 448H704V320c0-106.4-85.6-192-192-192S320 213.6 320 320h64c0-70.4 57.6-128 128-128s128 57.6 128 128v128H224c-17.6 0-32 14.4-32 32v384c0 17.6 14.4 32 32 32h576c17.6 0 32-14.4 32-32V480c0-17.6-14.4-32-32-32zM512 736c-35.2 0-64-28.8-64-64s28.8-64 64-64 64 28.8 64 64-28.8 64-64 64z" p-id="56888"></path></svg>
|
||||
|
After Width: | Height: | Size: 644 B |
1
src/assets/icons/dc/lock_1.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747121371122" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="57034" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M800 448H704V320c0-106.4-85.6-192-192-192S320 213.6 320 320v128H224c-17.6 0-32 14.4-32 32v384c0 17.6 14.4 32 32 32h576c17.6 0 32-14.4 32-32V480c0-17.6-14.4-32-32-32zM512 736c-35.2 0-64-28.8-64-64s28.8-64 64-64 64 28.8 64 64-28.8 64-64 64z m128-288H384V320c0-70.4 57.6-128 128-128s128 57.6 128 128v128z" p-id="57035"></path></svg>
|
||||
|
After Width: | Height: | Size: 663 B |
3
src/assets/icons/dc/star.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="15" viewBox="0 0 16 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.33333 3.41354L6.54479 0.519166C6.67799 0.340555 6.83611 0.209376 7.01917 0.125626C7.20208 0.0418756 7.39556 0 7.59958 0C7.80347 0 7.99438 0.0434724 8.17229 0.130417C8.35021 0.217222 8.50701 0.346805 8.64271 0.519166L10.8542 3.41354L14.2579 4.53521C14.5454 4.64104 14.7674 4.80819 14.9238 5.03667C15.0803 5.26514 15.1585 5.5175 15.1585 5.79375C15.1585 5.92528 15.14 6.05632 15.1029 6.18687C15.0658 6.31757 15.0048 6.44278 14.9198 6.5625L12.7179 9.57208L12.8013 12.835C12.8013 13.2182 12.6705 13.5428 12.409 13.809C12.1476 14.0752 11.8348 14.2083 11.4706 14.2083C11.4613 14.2083 11.344 14.1901 11.1185 14.1538L7.58333 13.149L4.04688 14.1546C3.97826 14.1818 3.91743 14.1976 3.86437 14.2019C3.81132 14.2062 3.75722 14.2083 3.70208 14.2083C3.33694 14.2083 3.01938 14.0752 2.74938 13.809C2.47951 13.5428 2.35153 13.2182 2.36542 12.835L2.44875 9.57208L0.23875 6.5625C0.152917 6.43986 0.0916667 6.31799 0.055 6.19688C0.0183333 6.07563 0 5.94729 0 5.81187C0 5.52576 0.0824306 5.26479 0.247292 5.02896C0.412014 4.79326 0.642083 4.62604 0.9375 4.52729L4.33333 3.41354ZM4.96 4.34458L1.27562 5.57208C1.19549 5.59875 1.14076 5.65354 1.11146 5.73646C1.08201 5.81924 1.09403 5.89535 1.1475 5.96479L3.53792 9.26375L3.44875 12.8606C3.44333 12.9515 3.47535 13.0235 3.54479 13.0769C3.61424 13.1303 3.69174 13.1438 3.77729 13.1171L7.58333 12.0192L11.4102 13.0963C11.4958 13.1229 11.5733 13.1095 11.6427 13.056C11.7122 13.0027 11.7442 12.9306 11.7388 12.8398L11.6267 9.24208L14.0192 5.94396C14.0726 5.87451 14.0847 5.7984 14.0552 5.71562C14.0259 5.63271 13.9712 5.57792 13.891 5.55125L10.1858 4.34458L7.78375 1.23729C7.73556 1.16771 7.66875 1.13292 7.58333 1.13292C7.49792 1.13292 7.43111 1.16771 7.38292 1.23729L4.96 4.34458Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
3
src/assets/icons/dc/triangle.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="14" height="12" viewBox="0 0 14 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.706857 12C0.414255 12 0.206211 11.8612 0.0827259 11.5837C-0.0406133 11.3062 -0.0260342 11.0369 0.126463 10.7758L6.41961 0.342906C6.56321 0.114302 6.75667 0 7 0C7.24333 0 7.43679 0.114302 7.58039 0.342906L13.8735 10.7758C14.026 11.0369 14.0406 11.3062 13.9173 11.5837C13.7938 11.8612 13.5857 12 13.2931 12H0.706857ZM1.53284 10.7327H12.4672L7 1.61777L1.53284 10.7327Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 497 B |
@@ -2,14 +2,31 @@
|
||||
<div class="header-tools">
|
||||
<template v-for="(v, i) in tools" :key="i">
|
||||
<span class="line" v-if="v.type === 'line'"></span>
|
||||
<span
|
||||
v-else
|
||||
class="icon"
|
||||
@click="onClickTool(v)"
|
||||
:class="{ active: v.name === tool, disabled: v.disabled }"
|
||||
>
|
||||
<svg-icon :name="v.icon" :size="v.iconSize" />
|
||||
</span>
|
||||
<div v-else class="item">
|
||||
<span
|
||||
class="icon"
|
||||
@click="onClickTool(v)"
|
||||
:class="{
|
||||
active: v.name === tool,
|
||||
disabled: v.disabled
|
||||
}"
|
||||
>
|
||||
<svg-icon :name="v.icon" :size="v.iconSize" />
|
||||
</span>
|
||||
<span class="more" v-if="v.child" @click="onClickMore(v)">
|
||||
<svg-icon name="dc-down_arrow2" size="7" />
|
||||
</span>
|
||||
<div v-if="v.child" class="child" v-show="v.showChild">
|
||||
<div v-for="(v_, i_) in v.child" :key="i_" @click="onClickTool(v_, v)">
|
||||
<span v-show="tool === v_.name" class="dui">
|
||||
<svg-icon name="dc-dui" size="9" />
|
||||
</span>
|
||||
<span class="icon"><svg-icon :name="v_.icon" :size="v_.iconSize" /></span>
|
||||
<span class="label">{{ v_.label }}</span>
|
||||
<span class="tip">{{ v_.tip }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<button class="export" @click="emit('export')">
|
||||
<span class="icon"><svg-icon name="export" size="12" /></span>
|
||||
@@ -42,48 +59,108 @@
|
||||
const toolManager = inject('toolManager') as any
|
||||
const objectManager = inject('objectManager') as any
|
||||
const canvasManager = inject('canvasManager') as any
|
||||
const layerManager = inject('layerManager') as any
|
||||
const tool = computed(() => toolManager.currentTool.value)
|
||||
const historyIndex = computed(() => stateManager.historyIndex.value)
|
||||
const historyList = computed(() => stateManager.historyList.value)
|
||||
const isUndo = computed(() => !historyList.value[historyIndex.value - 1])
|
||||
const isRedo = computed(() => !historyList.value[historyIndex.value + 1])
|
||||
const activeLayerId = computed(() => layerManager.activeID.value)
|
||||
const tools = ref([
|
||||
{ name: OperationType.SELECT, icon: 'dc-select', iconSize: 16, disabled: ref(false) },
|
||||
{ name: OperationType.PAN, icon: 'dc-move', iconSize: 18, disabled: ref(false) },
|
||||
{ name: OperationType.DRAW, icon: 'dc-brush', iconSize: 18, disabled: ref(false) },
|
||||
{ name: OperationType.ERASER, icon: 'dc-eraser', iconSize: 18, disabled: ref(false) },
|
||||
{ name: OperationType.SELECT, icon: 'dc-select', iconSize: 16 },
|
||||
{ name: OperationType.PAN, icon: 'dc-move', iconSize: 18 },
|
||||
{ name: OperationType.DRAW, icon: 'dc-brush', iconSize: 18 },
|
||||
{ name: OperationType.ERASER, icon: 'dc-eraser', iconSize: 18 },
|
||||
{ icon: 'dc-image', iconSize: 17, on: () => onImageClick() },
|
||||
{ name: OperationType.SELECTBOX, icon: 'dc-selectbox', iconSize: 16 },
|
||||
{
|
||||
name: OperationType.IMAGE,
|
||||
icon: 'dc-image',
|
||||
iconSize: 17,
|
||||
disabled: ref(false),
|
||||
on: () => onImageClick()
|
||||
name: OperationType.RECTANGLE,
|
||||
icon: 'dc-rectangle',
|
||||
iconSize: 16,
|
||||
showChild: false,
|
||||
child: [
|
||||
{
|
||||
name: OperationType.RECTANGLE,
|
||||
label: 'Rectangle',
|
||||
icon: 'dc-rectangle',
|
||||
iconSize: 13
|
||||
},
|
||||
{
|
||||
name: OperationType.LINE,
|
||||
label: 'Line',
|
||||
icon: 'dc-line',
|
||||
iconSize: 10
|
||||
},
|
||||
{
|
||||
name: OperationType.ARROW,
|
||||
label: 'Arrow',
|
||||
icon: 'dc-arrow',
|
||||
iconSize: 11
|
||||
},
|
||||
{
|
||||
name: OperationType.ELLIPSE,
|
||||
label: 'Ellipse',
|
||||
icon: 'dc-ellipse',
|
||||
iconSize: 15
|
||||
},
|
||||
{
|
||||
name: OperationType.TRIANGLE,
|
||||
label: 'Polygon',
|
||||
icon: 'dc-triangle',
|
||||
iconSize: 14
|
||||
},
|
||||
{
|
||||
name: OperationType.STAR,
|
||||
label: 'Star',
|
||||
icon: 'dc-star',
|
||||
iconSize: 15
|
||||
}
|
||||
]
|
||||
},
|
||||
{ name: OperationType.SELECTBOX, icon: 'dc-selectbox', iconSize: 16, disabled: ref(false) },
|
||||
{ name: OperationType.RECTANGLE, icon: 'dc-rectangle', iconSize: 16, disabled: ref(false) },
|
||||
|
||||
{ type: 'line' },
|
||||
{
|
||||
name: OperationType.UNDO,
|
||||
icon: 'dc-undo',
|
||||
iconSize: 18,
|
||||
disabled: isUndo,
|
||||
on: () => stateManager.undoState()
|
||||
},
|
||||
{
|
||||
name: OperationType.REDO,
|
||||
icon: 'dc-redo',
|
||||
iconSize: 18,
|
||||
disabled: isRedo,
|
||||
on: () => stateManager.redoState()
|
||||
},
|
||||
{
|
||||
name: 'copy',
|
||||
icon: 'dc-copy',
|
||||
iconSize: 16,
|
||||
disabled: computed(() => !activeLayerId.value),
|
||||
on: () => onCopyActiveLayer()
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
icon: 'dc-delete',
|
||||
iconSize: 18,
|
||||
disabled: computed(() => !activeLayerId.value),
|
||||
on: () => onDeleteActiveLayer()
|
||||
}
|
||||
])
|
||||
const onClickTool = (tool: any) => {
|
||||
if (tool.disabled?.value) return
|
||||
if (tool.on) {
|
||||
tool.on()
|
||||
} else {
|
||||
toolManager.setTool(tool.name)
|
||||
const onClickTool = (v: any, parent?: any) => {
|
||||
if (v.disabled?.value) return
|
||||
if (parent) {
|
||||
parent.name = v.name
|
||||
parent.icon = v.icon
|
||||
parent.iconSize = v.iconSize * 1.23
|
||||
}
|
||||
tools.value.forEach((v) => v.hasOwnProperty('showChild') && (v.showChild = false))
|
||||
v.on ? v.on() : toolManager.setTool(v.name)
|
||||
}
|
||||
const onClickMore = (v: any) => {
|
||||
tools.value.forEach((item) => {
|
||||
if (item.hasOwnProperty('showChild') && item.name !== v.name) item.showChild = false
|
||||
})
|
||||
v.showChild = !v.showChild
|
||||
}
|
||||
const onImageClick = async () => {
|
||||
const layer = await importLocalImage(false)
|
||||
@@ -91,6 +168,14 @@
|
||||
objectManager.setBlendMode(id, BlendMode.MULTIPLY)
|
||||
objectManager.setFillRepeat(id)
|
||||
}
|
||||
const onCopyActiveLayer = () => {
|
||||
if (!activeLayerId.value) return
|
||||
layerManager.copyLayerById(activeLayerId.value)
|
||||
}
|
||||
const onDeleteActiveLayer = () => {
|
||||
if (!activeLayerId.value) return
|
||||
layerManager.deleteLayerById(activeLayerId.value)
|
||||
}
|
||||
const onWorkbench = async () => {
|
||||
exportCanvasToImage(canvasManager.canvas).then((url) => {
|
||||
emit('workbench', { url })
|
||||
@@ -123,22 +208,81 @@
|
||||
border-radius: 0.2rem;
|
||||
margin: 0 0.6rem;
|
||||
}
|
||||
> .icon {
|
||||
cursor: pointer;
|
||||
> .item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
--svg-icon-color: #000;
|
||||
border-radius: 0.4rem;
|
||||
&:not(.disabled).active,
|
||||
&:not(.disabled):hover {
|
||||
background-color: #ebebeb;
|
||||
> span {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
--svg-icon-color: #000;
|
||||
border-radius: 0.4rem;
|
||||
height: 3rem;
|
||||
&:not(.disabled).active,
|
||||
&:not(.disabled):hover {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
> .icon {
|
||||
width: 3rem;
|
||||
}
|
||||
> .more {
|
||||
width: 1.1rem;
|
||||
}
|
||||
> .child {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
min-width: 21rem;
|
||||
top: calc(100% + 2rem);
|
||||
left: 0;
|
||||
padding: 0.6rem 0;
|
||||
border: 0.1remx solid #d9d9d9;
|
||||
background-color: #fff;
|
||||
border-radius: 1.2rem;
|
||||
box-shadow: 0 0.4rem 0.4rem 0 rgba(0, 0, 0, 0.25);
|
||||
> div {
|
||||
height: 3.2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// justify-content: center;
|
||||
gap: 0.8rem;
|
||||
position: relative;
|
||||
padding: 0 1.6rem;
|
||||
> .dui {
|
||||
position: absolute;
|
||||
left: 1.5rem;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
> .icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin-left: 0.8rem;
|
||||
}
|
||||
> .label {
|
||||
flex: 1;
|
||||
font-size: 1.4rem;
|
||||
color: #000;
|
||||
}
|
||||
> .tip {
|
||||
font-size: 1.3rem;
|
||||
color: rgba(13, 13, 13, 0.65);
|
||||
}
|
||||
&:not(.disabled).active,
|
||||
&:not(.disabled):hover {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> button {
|
||||
|
||||
@@ -33,6 +33,19 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label">Opacity</div>
|
||||
<div class="value">
|
||||
<depth-slider
|
||||
v-model="opacity"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:tipFormatter="(v) => v + '%'"
|
||||
@input="inputOpacity"
|
||||
@change="changeOpacity"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label">Gap X</div>
|
||||
<div class="value">
|
||||
@@ -101,6 +114,7 @@
|
||||
const gapX = ref(0)
|
||||
const gapY = ref(0)
|
||||
const offset = ref({ x: 0, y: 0 })
|
||||
const opacity = ref(100)
|
||||
|
||||
const updateData = async () => {
|
||||
await nextTick()
|
||||
@@ -113,6 +127,7 @@
|
||||
x: Math.round((fill.offsetX / props.object.width) * 100),
|
||||
y: Math.round((fill.offsetY / props.object.height) * 100)
|
||||
}
|
||||
opacity.value = Math.round(props.object.opacity * 100)
|
||||
}
|
||||
updateData()
|
||||
|
||||
@@ -138,6 +153,15 @@
|
||||
objectManager.updateFillRepeatGap(id.value, options, isRecord)
|
||||
}
|
||||
|
||||
const inputOpacity = () => setOpacity(false)
|
||||
const changeOpacity = () => setOpacity(true)
|
||||
const setOpacity = (isRecord: boolean) => {
|
||||
const options = {
|
||||
opacity: opacity.value / 100
|
||||
}
|
||||
objectManager.updateOpacity(id.value, options, isRecord)
|
||||
}
|
||||
|
||||
stateManager.event.add('canvas:undo', updateData)
|
||||
stateManager.event.add('canvas:redo', updateData)
|
||||
onBeforeUnmount(() => {
|
||||
|
||||
@@ -18,11 +18,15 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="icons">
|
||||
<span @click.stop="onClickLock">
|
||||
<svg-icon v-show="!layer.info.lock" name="dc-lock_0" size="15" />
|
||||
<svg-icon v-show="layer.info.lock" name="dc-lock_1" size="15" color="#1890ff" />
|
||||
</span>
|
||||
<span @click.stop="onClickShowHide"
|
||||
><svg-icon :name="layer.visible ? 'dc-show' : 'dc-hide'" size="15"
|
||||
/></span>
|
||||
<span @click.stop="onClickDelete"><svg-icon name="dc-delete" size="13" /></span>
|
||||
<span><svg-icon name="dc-down_arrow" size="11" /></span>
|
||||
<!-- <span><svg-icon name="dc-down_arrow" size="11" /></span> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -64,6 +68,10 @@
|
||||
const onClickLayer = () => {
|
||||
layerManager.setActiveID(props.layer.info.id)
|
||||
}
|
||||
const onClickLock = () => {
|
||||
const info = props.layer.info
|
||||
layerManager.setLayerLockById(info.id, !info.lock)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
layerManager.dragSort(data.info.id, newIndex)
|
||||
}
|
||||
const addLayer = () => {
|
||||
layerManager.createEmptyLayer()
|
||||
layerManager.createEmptyLayer(true, true)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -20,7 +20,13 @@ fabric.Object.prototype.toObject = function () {
|
||||
arr.push(...v)
|
||||
}
|
||||
})
|
||||
return this.toObject_(arr)
|
||||
const object = this.toObject_(arr)
|
||||
if (object.info) {
|
||||
let lock = !!object.info.lock
|
||||
object.evented = !lock
|
||||
object.selectable = !lock
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
interface CanvasInitOptions {
|
||||
@@ -71,9 +77,12 @@ export class CanvasManager {
|
||||
top: 0,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
evented: false,
|
||||
selectable: false,
|
||||
info: {
|
||||
id: createId("image"),
|
||||
name: "图片图层",
|
||||
lock: true,
|
||||
}
|
||||
})
|
||||
image = img
|
||||
@@ -171,7 +180,7 @@ export class CanvasManager {
|
||||
/** 设置激活对象 */
|
||||
setActiveObjectById(id: string) {
|
||||
const obj = this.getObjectById(id)
|
||||
if (obj) this.canvas.setActiveObject(obj)
|
||||
if (obj && obj.evented) this.canvas.setActiveObject(obj)
|
||||
this.renderAll()
|
||||
}
|
||||
resetZoom(animated = true, adaptive = true) {
|
||||
@@ -208,13 +217,13 @@ export class CanvasManager {
|
||||
getObjectById(id: string) {
|
||||
return this.getObjects().find((item: any) => item?.info?.id === id)
|
||||
}
|
||||
getActiveObject() {
|
||||
return this.getObjectById(this.layerManager.activeID.value)
|
||||
/** 获取选中对象 */
|
||||
getSelectedObject() {
|
||||
return this.canvas.getActiveObject()
|
||||
}
|
||||
renderAll() {
|
||||
this.canvas.renderAll()
|
||||
}
|
||||
|
||||
deleteObjectById(id: string) {
|
||||
const object = this.getObjectById(id)
|
||||
if (object) {
|
||||
@@ -223,6 +232,11 @@ export class CanvasManager {
|
||||
this.renderAll()
|
||||
}
|
||||
}
|
||||
/** 取消选中对象 */
|
||||
discardActiveObject() {
|
||||
this.canvas.discardActiveObject()
|
||||
this.renderAll()
|
||||
}
|
||||
// 拖拽排序
|
||||
dragSort(id, newIndex) {
|
||||
this.canvas.moveTo(this.getObjectById(id), newIndex)
|
||||
|
||||
@@ -3,6 +3,7 @@ import { fabric } from 'fabric-with-all'
|
||||
import { createId } from '../../tools/tools'
|
||||
import { exportObjectsToImage, exportObjectToThumbnail } from '../tools/exportMethod'
|
||||
import { OperationType } from '../tools/layerHelper'
|
||||
import { getArrowPath, cloneObjects, getStarArr } from '../tools/canvasMethod'
|
||||
|
||||
export class LayerManager {
|
||||
stateManager: any
|
||||
@@ -40,19 +41,46 @@ export class LayerManager {
|
||||
this.canvasManager.renderAll()
|
||||
}
|
||||
}
|
||||
|
||||
/** 设置图层显示状态 */
|
||||
setLayerVisibleById(id, visible: boolean) {
|
||||
const layer = this.getLayerById(id)
|
||||
if (layer) {
|
||||
layer.visible = visible
|
||||
}
|
||||
const object = this.canvasManager.getObjectById(id)
|
||||
if (object) {
|
||||
object.set({
|
||||
visible: visible
|
||||
})
|
||||
this.canvasManager.renderAll()
|
||||
}
|
||||
if (!layer || !object) return
|
||||
layer.visible = visible
|
||||
object.set({
|
||||
visible: visible
|
||||
})
|
||||
this.canvasManager.renderAll()
|
||||
this.stateManager.recordState()
|
||||
}
|
||||
/** 设置图层锁定状态 */
|
||||
setLayerLockById(id, lock: boolean) {
|
||||
const layer = this.getLayerById(id)
|
||||
const object = this.canvasManager.getObjectById(id)
|
||||
if (!layer || !object) return
|
||||
layer.info.lock = !!lock
|
||||
layer.evented = !lock
|
||||
object.info.lock = !!lock
|
||||
object.set({
|
||||
evented: !lock,
|
||||
selectable: !lock,
|
||||
})
|
||||
if (lock) {
|
||||
// 取消选中对象
|
||||
const e = this.canvasManager.getSelectedObject()
|
||||
if (e) {
|
||||
const objects = [...(e._objects || [e])]
|
||||
if (objects.some(v => v.info?.id === object.info.id)) {
|
||||
this.canvasManager.discardActiveObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
this.canvasManager.renderAll()
|
||||
this.stateManager.recordState()
|
||||
}
|
||||
|
||||
/** 删除指定图层 */
|
||||
deleteLayerById(id, isActive = true) {
|
||||
this.canvasManager.deleteObjectById(id)
|
||||
if (id === this.activeID.value && isActive) {
|
||||
@@ -60,6 +88,24 @@ export class LayerManager {
|
||||
}
|
||||
if (isActive) this.stateManager.recordState()
|
||||
}
|
||||
/** 复制指定图层 */
|
||||
copyLayerById(id) {
|
||||
const object = this.canvasManager.getObjectById(id)
|
||||
if (!object) return console.warn('复制图层失败,对象不存在ID:', id)
|
||||
cloneObjects([object]).then(objects => {
|
||||
const newObject = objects[0]
|
||||
const info = JSON.parse(JSON.stringify(newObject.info))
|
||||
info.id = createId("image")
|
||||
// info.name = info.name
|
||||
newObject.set({
|
||||
top: newObject.top + 15,
|
||||
left: newObject.left + 15,
|
||||
info: info,
|
||||
})
|
||||
this.canvasManager.add(newObject)
|
||||
this.setActiveID(newObject.info.id)
|
||||
})
|
||||
}
|
||||
// 拖拽排序
|
||||
dragSort(id, newIndex) {
|
||||
const index = Math.abs(this.layers.value.length - newIndex - 1)
|
||||
@@ -90,7 +136,7 @@ export class LayerManager {
|
||||
}
|
||||
}
|
||||
/** 创建空图层 */
|
||||
createEmptyLayer(isRecord = true) {
|
||||
createEmptyLayer(isRecord = true, isActive = false) {
|
||||
const emptyObject = new fabric.Rect({
|
||||
width: 0,
|
||||
height: 0,
|
||||
@@ -102,6 +148,7 @@ export class LayerManager {
|
||||
})
|
||||
this.setLayerPosition(emptyObject)
|
||||
this.canvasManager.add(emptyObject, isRecord)
|
||||
if (isActive) this.setActiveID(emptyObject.info.id, false)
|
||||
return emptyObject
|
||||
}
|
||||
/** 创建文本图层 */
|
||||
@@ -138,23 +185,109 @@ export class LayerManager {
|
||||
if (isActive) this.setActiveID(rectObject.info.id)
|
||||
return rectObject
|
||||
}
|
||||
/** 创建圆形图层 */
|
||||
async createCircleLayer(options?: any, isActive = false) {
|
||||
const circleObject = new fabric.Circle({
|
||||
/** 创建直线图层 */
|
||||
async createLineLayer(options?: any, isActive = false) {
|
||||
const line = [options?.x1 || 0, options?.y1 || 0, options?.x2 || 100, options?.y2 || 0]
|
||||
delete options.x1
|
||||
delete options.y1
|
||||
delete options.x2
|
||||
delete options.y2
|
||||
const lineObject = new fabric.Line(line, {
|
||||
stroke: 'black', // 线条颜色
|
||||
strokeWidth: 2, // 线条粗细
|
||||
...(options || {}),
|
||||
info: {
|
||||
id: createId("line"),
|
||||
name: '直线图层',
|
||||
...(options?.info || {}),
|
||||
}
|
||||
})
|
||||
this.setLayerPosition(lineObject, options)
|
||||
await this.canvasManager.add(lineObject)
|
||||
if (isActive) this.setActiveID(lineObject.info.id)
|
||||
return lineObject
|
||||
}
|
||||
/** 创建椭圆图层 */
|
||||
async createEllipseLayer(options?: any, isActive = false) {
|
||||
const ellipseObject = new fabric.Ellipse({
|
||||
radius: 50,
|
||||
fill: '#000',
|
||||
...(options || {}),
|
||||
info: {
|
||||
id: createId("circle"),
|
||||
name: '圆形图层',
|
||||
id: createId("ellipse"),
|
||||
name: '椭圆图层',
|
||||
...(options?.info || {}),
|
||||
}
|
||||
})
|
||||
this.setLayerPosition(circleObject, options)
|
||||
await this.canvasManager.add(circleObject)
|
||||
if (isActive) this.setActiveID(circleObject.info.id)
|
||||
return circleObject
|
||||
this.setLayerPosition(ellipseObject, options)
|
||||
await this.canvasManager.add(ellipseObject)
|
||||
if (isActive) this.setActiveID(ellipseObject.info.id)
|
||||
return ellipseObject
|
||||
}
|
||||
/** 创建三角形图层 */
|
||||
async createTriangleLayer(options?: any, isActive = false) {
|
||||
const triangleObject = new fabric.Triangle({
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#000',
|
||||
...(options || {}),
|
||||
info: {
|
||||
id: createId("triangle"),
|
||||
name: '三角形图层',
|
||||
...(options?.info || {}),
|
||||
}
|
||||
})
|
||||
this.setLayerPosition(triangleObject, options)
|
||||
await this.canvasManager.add(triangleObject)
|
||||
if (isActive) this.setActiveID(triangleObject.info.id)
|
||||
return triangleObject
|
||||
}
|
||||
/** 创建五角星图层 */
|
||||
async createStarLayer(options?: any, isActive = false) {
|
||||
const width = options?.width || 100
|
||||
const height = options?.height || 100
|
||||
delete options.points
|
||||
const starObject = new fabric.Polygon(getStarArr(width, height), {
|
||||
fill: '#000',
|
||||
...(options || {}),
|
||||
info: {
|
||||
id: createId("star"),
|
||||
name: '五角星图层',
|
||||
...(options?.info || {}),
|
||||
}
|
||||
})
|
||||
this.setLayerPosition(starObject, options)
|
||||
await this.canvasManager.add(starObject)
|
||||
if (isActive) this.setActiveID(starObject.info.id)
|
||||
return starObject
|
||||
}
|
||||
/** 创建箭头图层 */
|
||||
async createArrowLayer(options?: any, isActive = false) {
|
||||
const width = options?.width || 100
|
||||
const height = options?.height || 10
|
||||
delete options.width
|
||||
delete options.height
|
||||
const arrowObject = new fabric.Path(getArrowPath(width, height), {
|
||||
stroke: '#000', // 只设置边框颜色
|
||||
strokeWidth: 3, // 边框宽度
|
||||
fill: 'transparent', // 不填充
|
||||
strokeLineCap: 'round',
|
||||
strokeLineJoin: 'round',
|
||||
...(options || {}),
|
||||
info: {
|
||||
id: createId("star"),
|
||||
name: '箭头图层',
|
||||
...(options?.info || {}),
|
||||
}
|
||||
});
|
||||
this.setLayerPosition(arrowObject, options)
|
||||
this.canvasManager.add(arrowObject)
|
||||
if (isActive) this.setActiveID(arrowObject.info.id)
|
||||
return arrowObject
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 创建图片图层 */
|
||||
async createImageLayer(imgOrUrl: string | HTMLImageElement, options?: any, isRecord = true) {
|
||||
const canvasWidth = this.canvasManager.canvasWidth
|
||||
|
||||
@@ -207,5 +207,24 @@ export class ObjectManager {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 修改透明度
|
||||
* @param id 目标对象ID
|
||||
* @param options 透明度参数
|
||||
* @param options.opacity 透明度
|
||||
* @param isRecord 是否记录
|
||||
*/
|
||||
async updateOpacity(id: string, options: any, isRecord: boolean) {
|
||||
const object = this.getFillRepeatObject(id)
|
||||
if (!object) return null
|
||||
const opacity = options.opacity
|
||||
object.set("opacity", opacity);
|
||||
this.canvasManager.renderAll()
|
||||
if (isRecord) {
|
||||
this.stateManager.recordState()
|
||||
this.layerManager.updateLayerThumbnailsById(id)
|
||||
}
|
||||
}
|
||||
|
||||
dispose() { }
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { OperationType, OperationTypes } from "../tools/layerHelper";
|
||||
import { fabric } from 'fabric-with-all'
|
||||
/** 矩形工具管理器 */
|
||||
export class RectToolManager {
|
||||
@@ -10,6 +11,9 @@ export class RectToolManager {
|
||||
startX: number = 0
|
||||
startY: number = 0
|
||||
demoObject: any
|
||||
tools = [
|
||||
OperationType.RECTANGLE
|
||||
]
|
||||
constructor(options) {
|
||||
this.canvasManager = options.canvasManager
|
||||
this.stateManager = options.stateManager
|
||||
255
src/components/Canvas/DepthCanvas/manager/ShapeToolManager.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
import { OperationType, OperationTypes } from "../tools/layerHelper";
|
||||
import { getStarArr, getArrowPath, distance, angleBetweenPointsDegrees } from "../tools/canvasMethod";
|
||||
import { fabric } from 'fabric-with-all'
|
||||
/** 形状管理器 */
|
||||
export class ShapeToolManager {
|
||||
// 管理器
|
||||
canvasManager: any
|
||||
stateManager: any
|
||||
layerManager: any
|
||||
toolManager: any
|
||||
|
||||
isDragging: boolean = false
|
||||
startX: number = 0
|
||||
startY: number = 0
|
||||
demoObject: any
|
||||
tools = [
|
||||
OperationType.RECTANGLE,
|
||||
OperationType.LINE,
|
||||
OperationType.ARROW,
|
||||
OperationType.ELLIPSE,
|
||||
OperationType.TRIANGLE,
|
||||
OperationType.STAR,
|
||||
]
|
||||
constructor(options) {
|
||||
this.canvasManager = options.canvasManager
|
||||
this.stateManager = options.stateManager
|
||||
this.layerManager = options.layerManager
|
||||
this.toolManager = options.toolManager
|
||||
}
|
||||
mouseDownEvent(e) {
|
||||
this.isDragging = false
|
||||
this.demoObject = null
|
||||
|
||||
this.startX = e.absolutePointer.x
|
||||
this.startY = e.absolutePointer.y
|
||||
const currentTool = this.toolManager.currentTool.value
|
||||
if (currentTool === OperationType.RECTANGLE) {
|
||||
this.demoObject = this.downRectangle()
|
||||
} else if (currentTool === OperationType.LINE) {
|
||||
this.demoObject = this.downLine()
|
||||
} else if (currentTool === OperationType.ELLIPSE) {
|
||||
this.demoObject = this.downEllipse()
|
||||
} else if (currentTool === OperationType.TRIANGLE) {
|
||||
this.demoObject = this.downTriangle()
|
||||
} else if (currentTool === OperationType.STAR) {
|
||||
this.demoObject = this.downStar()
|
||||
} else if (currentTool === OperationType.ARROW) {
|
||||
this.demoObject = this.downArrow()
|
||||
}
|
||||
if (!this.demoObject) return;
|
||||
this.demoObject.set({
|
||||
evented: false,
|
||||
})
|
||||
this.demoObject.set
|
||||
this.isDragging = true
|
||||
this.canvasManager.canvas.add(this.demoObject)
|
||||
this.canvasManager.canvas.renderAll()
|
||||
}
|
||||
mouseMoveEvent(e) {
|
||||
if (!this.isDragging) return;
|
||||
var width = e.absolutePointer.x - this.startX
|
||||
var height = e.absolutePointer.y - this.startY
|
||||
var left = this.startX
|
||||
var top = this.startY
|
||||
if (width < 0) {
|
||||
left += width
|
||||
width = -width
|
||||
}
|
||||
if (height < 0) {
|
||||
top += height
|
||||
height = -height
|
||||
}
|
||||
const currentTool = this.toolManager.currentTool.value
|
||||
if (currentTool === OperationType.RECTANGLE) {
|
||||
this.moveRectangle({ width, height, left, top })
|
||||
} else if (currentTool === OperationType.LINE) {
|
||||
this.moveLine(e.absolutePointer)
|
||||
} else if (currentTool === OperationType.ELLIPSE) {
|
||||
this.moveEllipse({ width, height, left, top })
|
||||
} else if (currentTool === OperationType.TRIANGLE) {
|
||||
this.moveTriangle({ width, height, left, top })
|
||||
} else if (currentTool === OperationType.STAR) {
|
||||
this.moveStar({ width, height, left, top })
|
||||
} else if (currentTool === OperationType.ARROW) {
|
||||
this.moveArrow(e.absolutePointer)
|
||||
}
|
||||
this.demoObject.set({
|
||||
evented: false,
|
||||
})
|
||||
this.canvasManager.canvas.renderAll()
|
||||
}
|
||||
mouseUpEvent(e) {
|
||||
if (!this.isDragging) return;
|
||||
this.isDragging = false;
|
||||
const object = this.demoObject.toJSON("evented")
|
||||
const currentTool = this.toolManager.currentTool.value
|
||||
if (currentTool === OperationType.RECTANGLE) {
|
||||
this.upRectangle(object)
|
||||
} else if (currentTool === OperationType.LINE) {
|
||||
this.upLine(object)
|
||||
} else if (currentTool === OperationType.ELLIPSE) {
|
||||
this.upEllipse(object)
|
||||
} else if (currentTool === OperationType.TRIANGLE) {
|
||||
this.upTriangle(object)
|
||||
} else if (currentTool === OperationType.STAR) {
|
||||
this.upStar(object)
|
||||
} else if (currentTool === OperationType.ARROW) {
|
||||
this.upArrow(object)
|
||||
}
|
||||
this.canvasManager.canvas.remove(this.demoObject)
|
||||
this.demoObject = null
|
||||
this.canvasManager.canvas.renderAll()
|
||||
}
|
||||
|
||||
/** 绘制矩形 */
|
||||
downRectangle() {
|
||||
const rect = new fabric.Rect({
|
||||
left: this.startX,
|
||||
top: this.startY,
|
||||
width: 0,
|
||||
height: 0,
|
||||
fill: '#000',
|
||||
})
|
||||
return rect
|
||||
}
|
||||
moveRectangle({ width, height, left, top }) {
|
||||
this.demoObject.set({ width, height, left, top })
|
||||
|
||||
}
|
||||
upRectangle(object) {
|
||||
if (object.width === 0) object.width = 100
|
||||
if (object.height === 0) object.height = 100
|
||||
this.layerManager.createRectLayer(object, true)
|
||||
}
|
||||
|
||||
/** 绘制直线 */
|
||||
downLine() {
|
||||
const line = new fabric.Line([this.startX, this.startY, this.startX, this.startY], {
|
||||
stroke: 'black', // 线条颜色
|
||||
strokeWidth: 2 // 线条粗细
|
||||
})
|
||||
return line
|
||||
}
|
||||
moveLine({ x, y }) {
|
||||
this.demoObject.set({
|
||||
x1: this.startX,
|
||||
y1: this.startY,
|
||||
x2: x,
|
||||
y2: y,
|
||||
})
|
||||
}
|
||||
upLine(object) {
|
||||
this.layerManager.createLineLayer(object, true)
|
||||
}
|
||||
|
||||
/** 绘制椭圆 */
|
||||
downEllipse() {
|
||||
const circle = new fabric.Ellipse({
|
||||
left: this.startX,
|
||||
top: this.startY,
|
||||
fill: '#000',
|
||||
})
|
||||
return circle
|
||||
}
|
||||
moveEllipse({ width, height, left, top }) {
|
||||
this.demoObject.set({ rx: width / 2, ry: height / 2, left, top })
|
||||
}
|
||||
upEllipse(object) {
|
||||
if (object.rx === 0) object.rx = 50
|
||||
if (object.ry === 0) object.ry = 50
|
||||
this.layerManager.createEllipseLayer(object, true)
|
||||
}
|
||||
|
||||
|
||||
/** 绘制三角形 */
|
||||
downTriangle() {
|
||||
const triangle = new fabric.Triangle({
|
||||
left: this.startX,
|
||||
top: this.startY,
|
||||
width: 0,
|
||||
height: 0,
|
||||
fill: '#000',
|
||||
})
|
||||
return triangle
|
||||
}
|
||||
moveTriangle({ width, height, left, top }) {
|
||||
this.demoObject.set({ width, height, left, top })
|
||||
}
|
||||
upTriangle(object) {
|
||||
if (object.width === 0) object.width = 100
|
||||
if (object.height === 0) object.height = 100
|
||||
this.layerManager.createTriangleLayer(object, true)
|
||||
}
|
||||
|
||||
|
||||
/** 绘制五角星 */
|
||||
downStar() {
|
||||
const star = new fabric.Polygon(getStarArr(0, 0), {
|
||||
left: this.startX,
|
||||
top: this.startY,
|
||||
width: 0,
|
||||
height: 0,
|
||||
fill: '#000',
|
||||
strokeLineJoin: 'round', // 圆角连接
|
||||
strokeLineCap: 'round', // 圆角端点
|
||||
});
|
||||
|
||||
return star
|
||||
}
|
||||
moveStar({ width, height, left, top }) {
|
||||
this.demoObject.set({ left, top, width, height, points: getStarArr(width, height) })
|
||||
}
|
||||
upStar(object) {
|
||||
if (object.width === 0) object.width = 100
|
||||
if (object.height === 0) object.height = 100
|
||||
this.layerManager.createStarLayer(object, true)
|
||||
}
|
||||
|
||||
/** 绘制箭头 */
|
||||
downArrow() {
|
||||
return new fabric.Path();
|
||||
}
|
||||
moveArrow({ x, y }) {
|
||||
const width = distance(this.startX, this.startY, x, y)
|
||||
const angle = angleBetweenPointsDegrees(this.startX, this.startY, x, y)
|
||||
this.canvasManager.canvas.remove(this.demoObject)
|
||||
const arrow = new fabric.Path(getArrowPath(width, 10), {
|
||||
left: this.startX,
|
||||
top: this.startY,
|
||||
stroke: '#000', // 只设置边框颜色
|
||||
strokeWidth: 3, // 边框宽度
|
||||
fill: 'transparent', // 不填充
|
||||
strokeLineCap: 'round',
|
||||
strokeLineJoin: 'round',
|
||||
originY: 'center',
|
||||
angle: angle,
|
||||
});
|
||||
this.canvasManager.canvas.add(arrow)
|
||||
this.demoObject = arrow
|
||||
}
|
||||
upArrow(object) {
|
||||
if (object.originY !== "center") {
|
||||
this.layerManager.createArrowLayer({
|
||||
left: this.startX,
|
||||
top: this.startY,
|
||||
}, true)
|
||||
} else {
|
||||
this.layerManager.createArrowLayer(object, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
dispose() { }
|
||||
}
|
||||
@@ -54,6 +54,32 @@ export class ToolManager {
|
||||
name: OperationType.RECTANGLE,
|
||||
cursor: "crosshair",
|
||||
},
|
||||
/** 直线工具 */
|
||||
{
|
||||
name: OperationType.LINE,
|
||||
cursor: "crosshair",
|
||||
},
|
||||
/** 箭头工具 */
|
||||
{
|
||||
name: OperationType.ARROW,
|
||||
cursor: "crosshair",
|
||||
},
|
||||
/** 椭圆工具 */
|
||||
{
|
||||
name: OperationType.ELLIPSE,
|
||||
cursor: "crosshair",
|
||||
},
|
||||
/** 三角形工具 */
|
||||
{
|
||||
name: OperationType.TRIANGLE,
|
||||
cursor: "crosshair",
|
||||
},
|
||||
/** 五角星工具 */
|
||||
{
|
||||
name: OperationType.STAR,
|
||||
cursor: "crosshair",
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
onMounted() {
|
||||
@@ -91,7 +117,10 @@ export class ToolManager {
|
||||
// 切换工具时,设置画布事件
|
||||
setCanvasEvented(value: boolean) {
|
||||
this.canvasManager.canvas.selection = value
|
||||
this.canvasManager.canvas.getObjects().forEach((v) => v.evented = value)
|
||||
this.canvasManager.canvas.getObjects().forEach((v) => {
|
||||
if (v.info?.lock) return
|
||||
v.evented = value
|
||||
})
|
||||
}
|
||||
/** 选择工具 */
|
||||
setupSelectTool() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isBoolean } from "lodash-es";
|
||||
import { OperationType, OperationTypes } from "../../tools/layerHelper";
|
||||
import { RectToolManager } from "../RectToolManager"
|
||||
import { ShapeToolManager } from "../ShapeToolManager"
|
||||
import { AISelectboxToolManager } from "../AISelectboxToolManager"
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ export class CanvasEventManager {
|
||||
toolManager: this.toolManager,
|
||||
layerManager: this.layerManager,
|
||||
}
|
||||
this.rectToolManager = new RectToolManager(managers)
|
||||
this.shapeToolManager = new ShapeToolManager(managers)
|
||||
this.aiSelectboxToolManager = new AISelectboxToolManager(managers)
|
||||
|
||||
// 初始化所有事件
|
||||
@@ -209,9 +209,9 @@ export class CanvasEventManager {
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
this.aiSelectboxToolManager.mouseDownEvent(opt);
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseDownEvent(opt);
|
||||
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||
// 形状模式
|
||||
this.shapeToolManager.mouseDownEvent(opt);
|
||||
} else if (opt.e.altKey || opt.e.which === 2 || currentTool === OperationType.PAN) {
|
||||
this.canvas.isDragging = true;
|
||||
this.canvas.lastPosX = opt.e.clientX;
|
||||
@@ -237,9 +237,9 @@ export class CanvasEventManager {
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
this.aiSelectboxToolManager.mouseMoveEvent(opt);
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseMoveEvent(opt);
|
||||
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||
// 形状模式
|
||||
this.shapeToolManager.mouseMoveEvent(opt);
|
||||
} else if (this.canvas.isDragging) {
|
||||
const vpt = this.canvas.viewportTransform;
|
||||
vpt[4] += opt.e.clientX - this.canvas.lastPosX;
|
||||
@@ -321,9 +321,9 @@ export class CanvasEventManager {
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseDownEvent(opt);
|
||||
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||
// 形状模式
|
||||
this.shapeToolManager.mouseDownEvent(opt);
|
||||
} else if (currentTool === OperationType.PAN) {
|
||||
|
||||
// 平滑停止任何正在进行的惯性动画
|
||||
@@ -386,9 +386,9 @@ export class CanvasEventManager {
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
this.aiSelectboxToolManager.mouseMoveEvent(opt);
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseMoveEvent(opt);
|
||||
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||
// 形状模式
|
||||
this.shapeToolManager.mouseMoveEvent(opt);
|
||||
} else if (currentTool === OperationType.PAN) {
|
||||
|
||||
// 检查是否是触摸事件
|
||||
@@ -496,9 +496,9 @@ export class CanvasEventManager {
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseUpEvent(opt);
|
||||
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||
// 形状模式
|
||||
this.shapeToolManager.mouseUpEvent(opt);
|
||||
} else if (currentTool === OperationType.PAN) {
|
||||
|
||||
// 重置触摸状态
|
||||
@@ -668,9 +668,9 @@ export class CanvasEventManager {
|
||||
} else if (currentTool === OperationType.SELECTBOX) {
|
||||
// 选择框模式
|
||||
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
||||
} else if (currentTool === OperationType.RECTANGLE) {
|
||||
// 矩形模式
|
||||
this.rectToolManager.mouseUpEvent(opt);
|
||||
} else if (this.shapeToolManager.tools.includes(currentTool)) {
|
||||
// 形状模式
|
||||
this.shapeToolManager.mouseUpEvent(opt);
|
||||
} else if (this.canvas.isDragging) {
|
||||
// if (this.lastMousePositions.length > 1 && opt && opt.e) {
|
||||
// this.animationManager.applyInertiaEffect(
|
||||
@@ -1074,7 +1074,7 @@ export class CanvasEventManager {
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.rectToolManager?.dispose()
|
||||
this.shapeToolManager?.dispose()
|
||||
this.aiSelectboxToolManager?.dispose()
|
||||
// 移除所有事件监听
|
||||
this.canvas.off();
|
||||
|
||||
@@ -9,13 +9,14 @@ export class KeyEventManager {
|
||||
/** 处理键盘事件 */
|
||||
_handleKeyDown: any
|
||||
handleKeyDown(event: any) {
|
||||
const activeID = this.stateManager.layerManager.activeID.value
|
||||
const ctrl = event.ctrlKey ? 'ctrl-' : "";
|
||||
const shift = event.shiftKey ? 'shift-' : "";
|
||||
const key = event.key;
|
||||
const reg = new RegExp(`^${ctrl}${shift}${key}$`, 'i')
|
||||
const list = [
|
||||
// { key: "ctrl-c", handler: () => this.handleCopy(event) },
|
||||
// { key: "delete", handler: () => this.handleDelete(event) },
|
||||
{ key: "ctrl-c", handler: () => this.stateManager.layerManager.copyLayerById(activeID) },
|
||||
{ key: "delete", handler: () => this.stateManager.layerManager.deleteLayerById(activeID) },
|
||||
{ key: "ctrl-z", handler: () => this.stateManager.undoState() },
|
||||
{ key: "ctrl-shift-z", handler: () => this.stateManager.redoState() },
|
||||
]
|
||||
|
||||
@@ -39,4 +39,57 @@ export async function getObjectsBoundingBox(objects = []) {
|
||||
width: box2.x - box1.x,
|
||||
height: box2.y - box1.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取五角星数组 */
|
||||
export function getStarArr(width = 0, height = 0) {
|
||||
const arr = [
|
||||
{ x: 0, y: -0.5 }, // 顶点0 (上)
|
||||
{ x: 0.15, y: -0.15 }, // 顶点1 (内)
|
||||
{ x: 0.50, y: -0.15 }, // 顶点2 (右上外)
|
||||
{ x: 0.20, y: 0.10 }, // 顶点3 (内)
|
||||
{ x: 0.30, y: 0.50 }, // 顶点4 (右下外)
|
||||
{ x: 0.0, y: 0.25 }, // 顶点5 (内)
|
||||
{ x: -0.30, y: 0.50 }, // 顶点6 (左下外)
|
||||
{ x: -0.20, y: 0.10 }, // 顶点7 (内)
|
||||
{ x: -0.50, y: -0.15 }, // 顶点8 (左上外)
|
||||
{ x: -0.15, y: -0.15 } // 顶点9 (内)
|
||||
]
|
||||
return arr.map(item => ({
|
||||
x: item.x * width,
|
||||
y: item.y * height,
|
||||
}))
|
||||
}
|
||||
/** 获取箭头路径 */
|
||||
export function getArrowPath(width = 0, height = 0) {
|
||||
const arr = [
|
||||
["M", 0, height / 2],
|
||||
["L", width, height / 2],
|
||||
["M", width - 8, 0],
|
||||
["L", width, height / 2],
|
||||
["L", width - 8, height],
|
||||
]
|
||||
var path = ""
|
||||
arr.forEach(item => {
|
||||
path += item.join(" ") + " "
|
||||
})
|
||||
return path
|
||||
}
|
||||
|
||||
/** 计算两点之间的距离 */
|
||||
export function distance(x1, y1, x2, y2) {
|
||||
const dx = x2 - x1;
|
||||
const dy = y2 - y1;
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
/** 计算两点之间的角度(角度) */
|
||||
export function angleBetweenPointsDegrees(x1, y1, x2, y2) {
|
||||
const dx = x2 - x1;
|
||||
const dy = y2 - y1;
|
||||
|
||||
// 计算弧度并转换为角度
|
||||
const rad = Math.atan2(dy, dx);
|
||||
const deg = rad * 180 / Math.PI;
|
||||
|
||||
return deg;
|
||||
}
|
||||
|
||||
@@ -29,12 +29,16 @@ export const OperationType = {
|
||||
PAN: "pan", // 拖拽模式
|
||||
DRAW: "draw", // 绘画模式
|
||||
ERASER: "eraser", // 橡皮擦模式
|
||||
IMAGE: "image",// 图片工具模式
|
||||
SELECTBOX: "selectbox",// 选择框工具模式
|
||||
|
||||
RECTANGLE: "rectangle",// 矩形工具模式
|
||||
LINE: "line",// 直线工具模式
|
||||
ARROW: "arrow",// 箭头工具模式
|
||||
ELLIPSE: "ellipse",// 椭圆工具模式
|
||||
TRIANGLE: "triangle",// 三角形工具模式
|
||||
STAR: "star",// 五角星工具模式
|
||||
|
||||
TEXT: "text",// 文字工具模式
|
||||
UNDO: "undo",// 撤销工具模式
|
||||
REDO: "redo",// 重做工具模式
|
||||
};
|
||||
|
||||
// 所有操作模式类型列表
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<!-- 颜色调色板 -->
|
||||
<div class="color-palette">
|
||||
<p class="label">Mode</p>
|
||||
<my-select v-model="data.mode" :list="modeList" />
|
||||
<p class="label">Choose Color</p>
|
||||
<div class="color-list">
|
||||
<div
|
||||
@@ -24,7 +26,8 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted, ref } from 'vue'
|
||||
const data = reactive({
|
||||
colors: []
|
||||
colors: [],
|
||||
mode: 'Advanced',
|
||||
})
|
||||
const emit = defineEmits(['update-data'])
|
||||
const maxColor = 5
|
||||
@@ -41,11 +44,15 @@
|
||||
const target = e.target as HTMLInputElement
|
||||
data.colors.push(target.value)
|
||||
}
|
||||
|
||||
const modeList = ref([
|
||||
{ value: 'Advanced', label: 'Advanced' },
|
||||
{ value: 'Normal', label: 'Normal' }
|
||||
])
|
||||
const getApiData = ()=>{
|
||||
return {
|
||||
variantCount: '2',
|
||||
colors: data.colors
|
||||
colors: data.colors,
|
||||
mode: data.mode,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
import To3View from './to-3view.vue'
|
||||
import To3DModel from './to-3d-model.vue'
|
||||
|
||||
import { toRealStyleApi, toColorPaletteApi, toSceneCompositionApi } from '@/api/flow-canvas'
|
||||
import { toRealStyleApi, toColorPaletteApi, toSceneCompositionApi, sketchAddPrintApi, sketchToThreeApi, threeToThreeViewsApi } from '@/api/flow-canvas'
|
||||
|
||||
// import ToVideo from './to-video.vue'
|
||||
// import AddPrint from './add-print.vue'
|
||||
@@ -62,6 +62,7 @@
|
||||
type: NODE_DATATYPE.SURFACE_EDIT,
|
||||
title: 'Surface Edit',
|
||||
component: SurfaceEdit,
|
||||
api: sketchAddPrintApi
|
||||
},
|
||||
{
|
||||
tier: NODE_DATATIER.SCENE_COMPOSITION,
|
||||
@@ -81,13 +82,15 @@
|
||||
tier: NODE_DATATIER.TO_3D_MODEL,
|
||||
type: NODE_DATATYPE.TO_3D_MODEL,
|
||||
title: 'To 3D Model',
|
||||
component: To3DModel
|
||||
component: To3DModel,
|
||||
api:sketchToThreeApi
|
||||
},
|
||||
{
|
||||
tier: NODE_DATATIER.TO_3VIEW,
|
||||
type: NODE_DATATYPE.TO_3VIEW,
|
||||
title: 'To 3-View',
|
||||
component: To3View
|
||||
component: To3View,
|
||||
api:threeToThreeViewsApi
|
||||
}
|
||||
]
|
||||
const nodeManager = inject('nodeManager') as any
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<div class="scene-composition">
|
||||
<p class="label">Prompt</p>
|
||||
<my-textarea v-model="data.prompt" />
|
||||
<p class="label">Mode</p>
|
||||
<my-select v-model="data.mode" :list="modeList" />
|
||||
<p class="label">Choose Style</p>
|
||||
<div class="style-list">
|
||||
<div
|
||||
@@ -47,8 +49,13 @@
|
||||
])
|
||||
const data = reactive({
|
||||
prompt: '',
|
||||
styles: ['Colorful', 'Modernist']
|
||||
styles: ['Colorful', 'Modernist'],
|
||||
mode: 'Advanced',
|
||||
})
|
||||
const modeList = ref([
|
||||
{ value: 'Advanced', label: 'Advanced' },
|
||||
{ value: 'Normal', label: 'Normal' }
|
||||
])
|
||||
const onClickStyle = (value: string) => {
|
||||
if (data.styles.includes(value)) {
|
||||
data.styles = data.styles.filter((v) => v !== value)
|
||||
@@ -60,6 +67,7 @@
|
||||
return {
|
||||
styles: data.styles,
|
||||
userPrompt: data.prompt,
|
||||
mode: data.mode,
|
||||
}
|
||||
}
|
||||
defineExpose({ data, getApiData })
|
||||
|
||||
@@ -3,21 +3,35 @@
|
||||
<div class="surface-edit">
|
||||
<p class="label">Image</p>
|
||||
<upload-file v-model="data.file" />
|
||||
<p class="label">Mode</p>
|
||||
<my-select v-model="data.mode" :list="modeList" />
|
||||
<p class="label">Prompt</p>
|
||||
<my-textarea v-model="data.prompt" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted } from 'vue'
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import myTextarea from '../../tools/my-textarea.vue'
|
||||
import uploadFile from '../../tools/upload-file.vue'
|
||||
import mySelect from '../../tools/my-select.vue'
|
||||
const data = reactive({
|
||||
prompt: '',
|
||||
file: null
|
||||
file: null,
|
||||
mode: 'Advanced',
|
||||
})
|
||||
|
||||
defineExpose({ data })
|
||||
const modeList = ref([
|
||||
{ value: 'Advanced', label: 'Advanced' },
|
||||
{ value: 'Normal', label: 'Normal' }
|
||||
])
|
||||
const getApiData = ()=>{
|
||||
return {
|
||||
mode: data.mode,
|
||||
surFaceUrl: data.file,
|
||||
userPrompt: data.prompt,
|
||||
}
|
||||
}
|
||||
defineExpose({ data,getApiData })
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -3,30 +3,38 @@
|
||||
<div class="to-3d-model">
|
||||
<p class="label">Image</p>
|
||||
<div class="image">
|
||||
<img src="https://s3-alpha-sig.figma.com/img/ea2f/590e/9638f62a2fc91e31f33db0022db1642c?Expires=1773014400&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=M0B8oJJOk~dGG0aZAqOIocAp7T0LFdJ9FYmCrEZVTCRzYxM6SJRNtYMTX-rTO3Z~s14QINh~o-S41XiZnBv-0zcKjuWot~VVaNHfd0~1LesfNe2KwvCinT~72btFut1pheLnKE-wWCX5ewtonxU77bnw386YPMTqv7DBZzksf2udsJA7NmOYD6~TUG3Q2dWSt~zPH~lkaidscPqpCnCbqzljCEi4RiHY4U3A45l5XypcX2umqn1UaYUFCTqV9471J4qdB6Dg2pcKocdp-7-3s1De6Q~2SmBOrSgDQ~KEADCB2lhKfhxgWmy0lwMvhTd4l90ygVZDWZRABgjHNrGUvg__" alt="">
|
||||
<img :src="data.url" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted } from 'vue'
|
||||
import { reactive, onMounted, useAttrs } from 'vue'
|
||||
import uploadFile from '../../tools/upload-file.vue'
|
||||
const data = reactive({
|
||||
file: null
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
|
||||
defineExpose({ data })
|
||||
const data = reactive({
|
||||
url: attrs.node?.data?.originalImage,
|
||||
})
|
||||
const getApiData = ()=>{
|
||||
return {
|
||||
imageUrls: [data.url],
|
||||
}
|
||||
}
|
||||
defineExpose({ data,getApiData })
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.to-3d-model {
|
||||
> .image {
|
||||
padding: 18px;
|
||||
padding: 6px;
|
||||
border-radius: 10px;
|
||||
background-color: #f0f0f0;
|
||||
>img{
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: 87px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,28 @@
|
||||
<div class="to-3view">
|
||||
<p class="label">3D Model</p>
|
||||
<div class="image">
|
||||
<img src="https://s3-alpha-sig.figma.com/img/ea2f/590e/9638f62a2fc91e31f33db0022db1642c?Expires=1773014400&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=M0B8oJJOk~dGG0aZAqOIocAp7T0LFdJ9FYmCrEZVTCRzYxM6SJRNtYMTX-rTO3Z~s14QINh~o-S41XiZnBv-0zcKjuWot~VVaNHfd0~1LesfNe2KwvCinT~72btFut1pheLnKE-wWCX5ewtonxU77bnw386YPMTqv7DBZzksf2udsJA7NmOYD6~TUG3Q2dWSt~zPH~lkaidscPqpCnCbqzljCEi4RiHY4U3A45l5XypcX2umqn1UaYUFCTqV9471J4qdB6Dg2pcKocdp-7-3s1De6Q~2SmBOrSgDQ~KEADCB2lhKfhxgWmy0lwMvhTd4l90ygVZDWZRABgjHNrGUvg__" alt="">
|
||||
<img :src="data.url" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted } from 'vue'
|
||||
const data = reactive({})
|
||||
|
||||
defineExpose({ data })
|
||||
import { reactive, useAttrs, inject, computed } from 'vue'
|
||||
const attrs = useAttrs()
|
||||
const stateManager = inject('stateManager') as any
|
||||
const data = reactive({
|
||||
url: attrs.node.data.originalImage,
|
||||
})
|
||||
const getApiData = ()=>{
|
||||
let glbUrl = null
|
||||
const superiorNode = stateManager.nodes.value.filter((item:any)=>item.id === attrs.node?.data?.superiorID)[0]
|
||||
const nodeData:any = superiorNode?.data?.data
|
||||
glbUrl = nodeData.imageProcessTasks.filter((item:any)=>item.taskId === nodeData.selectTaskId)[0]?.glbPath
|
||||
return {
|
||||
glbUrl: glbUrl,
|
||||
}
|
||||
}
|
||||
defineExpose({ data,getApiData })
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -48,13 +48,13 @@
|
||||
}
|
||||
])
|
||||
const modeList = ref([
|
||||
{ value: 'Fast', label: 'Fast' },
|
||||
{ value: 'Advanced', label: 'Advanced' },
|
||||
{ value: 'Normal', label: 'Normal' }
|
||||
])
|
||||
const data = reactive({
|
||||
prompt: '',
|
||||
pixelRatio: '1:1',
|
||||
mode: 'Normal',
|
||||
mode: 'Advanced',
|
||||
})
|
||||
|
||||
const getApiData = ()=>{
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<span class="icon">
|
||||
<svg-icon name="chat-compose" size="20" size-unit="px" />
|
||||
</span>
|
||||
<span class="icon" @click="onPreview(item?.url)">
|
||||
<span class="icon" @click="onPreview(item)">
|
||||
<svg-icon name="expand-lg" size="20" size-unit="px" />
|
||||
</span>
|
||||
<span class="icon" @click="onDownload(item?.url)">
|
||||
@@ -177,12 +177,11 @@
|
||||
}
|
||||
}
|
||||
])
|
||||
const onPreview = (url: string) => {
|
||||
console.log(data.superiorNodeType == NODE_DATATYPE.TO_3D_MODEL)
|
||||
const onPreview = (item: any) => {
|
||||
if(data.superiorNodeType == NODE_DATATYPE.TO_3D_MODEL){
|
||||
openThreeModelPreview(url)
|
||||
openThreeModelPreview({url:item?.glbPath,glbInfoObj:item?.glbInfoObj})
|
||||
}else{
|
||||
openImagePreview(url)
|
||||
openImagePreview(item.url)
|
||||
}
|
||||
}
|
||||
const onDownload = (url: string) => {
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||
//const props = defineProps({
|
||||
//})
|
||||
import { downloadImage } from '../../../../tools/tools'
|
||||
const props = defineProps({
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
let data = reactive({
|
||||
})
|
||||
const onDownload = () => {
|
||||
if(props?.config?.glbPath)downloadImage(props?.config?.glbPath, 'model.glb')
|
||||
}
|
||||
onMounted(()=>{
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
@@ -33,15 +41,15 @@ const {} = toRefs(data);
|
||||
<div class="flex">
|
||||
<div>
|
||||
<div class="fs14 c18">X</div>
|
||||
<div class="fs12 c66">1.0</div>
|
||||
<div class="fs12 c66">{{ config?.glbInfoObj?.centroid?.[0].toFixed(2) || 0 }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fs14 c18">Y</div>
|
||||
<div class="fs12 c66">1.0</div>
|
||||
<div class="fs12 c66">{{ config?.glbInfoObj?.centroid?.[1].toFixed(2) || 0 }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fs14 c18">Z</div>
|
||||
<div class="fs12 c66">1.0</div>
|
||||
<div class="fs12 c66">{{ config?.glbInfoObj?.centroid?.[2].toFixed(2) || 0 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fs14 c18">
|
||||
@@ -50,19 +58,19 @@ const {} = toRefs(data);
|
||||
<div class="flex">
|
||||
<div>
|
||||
<div class="fs14 c18">Height</div>
|
||||
<div class="fs12 c66">22</div>
|
||||
<div class="fs12 c66">{{ config?.glbInfoObj?.size?.[0].toFixed(2) || 0 }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fs14 c18">Width</div>
|
||||
<div class="fs12 c66">22</div>
|
||||
<div class="fs12 c66">{{ config?.glbInfoObj?.size?.[1].toFixed(2) || 0 }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fs14 c18">Depth</div>
|
||||
<div class="fs12 c66">22</div>
|
||||
<div class="fs12 c66">{{ config?.glbInfoObj?.size?.[2].toFixed(2) || 0 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="download">{{ $t('threeModel.download') }}</div>
|
||||
<div class="download" @click="onDownload">{{ $t('threeModel.download') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -6,9 +6,9 @@ import model from './model.vue'
|
||||
import detail from './detail.vue'
|
||||
|
||||
const props = defineProps({
|
||||
currentUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
currentData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
//const emit = defineEmits([
|
||||
@@ -18,8 +18,8 @@ let data = reactive({
|
||||
|
||||
const modelRef = ref(null)
|
||||
onMounted(()=>{
|
||||
console.log(props.currentUrl)
|
||||
modelRef.value.open(threeGlb)
|
||||
console.log(props?.currentData?.url)
|
||||
if(props?.currentData?.url)modelRef.value.open(props?.currentData?.url)
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
@@ -32,7 +32,7 @@ const {} = toRefs(data);
|
||||
<model ref="modelRef" />
|
||||
</div>
|
||||
<div class="detailBox">
|
||||
<detail ref="detailRef" />
|
||||
<detail ref="detailRef" :config="currentData" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, computed } from 'vue'
|
||||
import { uploadImage } from '@/api/upload'
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
const props = defineProps({
|
||||
modelValue: { type: [File, Object, String, null] },
|
||||
@@ -44,7 +45,12 @@
|
||||
input.accept = 'image/png, image/jpeg, image/jpg'
|
||||
input.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0]
|
||||
if (file) onChange(file)
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
uploadImage(formData).then((res) => {
|
||||
if (res) onChange(res)
|
||||
})
|
||||
})
|
||||
input.click()
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
/>
|
||||
<image-preview ref="imagePreviewRef" />
|
||||
<baseModal ref="threeModelRef">
|
||||
<template v-slot="{ currentUrl }">
|
||||
<threeModel :currentUrl="currentUrl" />
|
||||
<template v-slot="{ currentData }">
|
||||
<threeModel :currentData="currentData" />
|
||||
</template>
|
||||
</baseModal>
|
||||
</template>
|
||||
@@ -81,6 +81,7 @@
|
||||
import resultImage from './components/nodes/result-image.vue'
|
||||
import card from './components/nodes/cards/index.vue'
|
||||
import text from './components/nodes/text.vue'
|
||||
import { downImgListToZip } from '../tools/tools'
|
||||
|
||||
const components = {
|
||||
[NODE_COMPONENT.RESULT_IMAGE]: resultImage,
|
||||
@@ -194,6 +195,20 @@
|
||||
return JSON.stringify(stateManager.nodes.value)
|
||||
}
|
||||
const exportFlow = () => {
|
||||
// console.log(vueFlow.value)
|
||||
// console.log(vueFlow.value.toImage)
|
||||
let arr = stateManager.nodes.value.filter((v) => v.data.type === NODE_COMPONENT.RESULT_IMAGE)
|
||||
let imgList = []
|
||||
arr.forEach((v) => {
|
||||
v.data.data.imageProcessTasks.forEach((item,index) => {
|
||||
let url = item.url
|
||||
let name = url?.split(".").pop().split("?").shift();
|
||||
imgList.push({url:url,name:`${v.data.type}${index == 0?'':index}.${name}`})
|
||||
})
|
||||
})
|
||||
downImgListToZip(imgList)
|
||||
console.log(imgList)
|
||||
return
|
||||
// flowManager.exportFlow()
|
||||
const str = getFlowJson()
|
||||
stateManager.isSave.value = false
|
||||
@@ -224,8 +239,8 @@
|
||||
imagePreviewRef.value.open(url)
|
||||
}
|
||||
/** 打开3D预览 */
|
||||
const openThreeModelPreview = (url: string) => {
|
||||
threeModelRef.value.open(url)
|
||||
const openThreeModelPreview = (currentData) => {
|
||||
threeModelRef.value.open(currentData)
|
||||
}
|
||||
provide('openImagePreview', openImagePreview)
|
||||
provide('openThreeModelPreview', openThreeModelPreview)
|
||||
|
||||
@@ -7,16 +7,18 @@
|
||||
<script setup lang="ts">
|
||||
import FullscreenDialog from '../components/fullscreen-dialog.vue'
|
||||
import flowCanvas from './flow-canvas.vue'
|
||||
import { ref } from 'vue'
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { getSketchFlowCanvas, putSketchFlowCanvas } from '@/api/flow-canvas'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const config = ref({}) as any
|
||||
const flowCanvasRef = ref<any>()
|
||||
const {t:$t} = useI18n()
|
||||
const open = async (options) => {
|
||||
let json = []
|
||||
await new Promise((resolve) => {
|
||||
getSketchFlowCanvas({ id: options.imgId }).then((res:any) => {
|
||||
getSketchFlowCanvas({ id: options.imgId },true).then((res:any) => {
|
||||
if (res) {
|
||||
json = JSON.parse(res)
|
||||
}
|
||||
@@ -34,7 +36,7 @@
|
||||
await new Promise((resolve) => {
|
||||
putSketchFlowCanvas({
|
||||
id: config.value.imgId,
|
||||
canvasData: str }).then(() => {
|
||||
canvasData: str },true).then(() => {
|
||||
resolve(true)
|
||||
}).catch(() => {
|
||||
resolve(true)
|
||||
@@ -47,6 +49,21 @@
|
||||
await exportFlow(str)
|
||||
dialogVisible.value = false
|
||||
}
|
||||
const handleBeforeUnload = (event) => {
|
||||
const str = flowCanvasRef.value?.getFlowJson()
|
||||
if (str) {
|
||||
event.preventDefault()
|
||||
event.returnValue = $t('flowCanvas.confirmLeave')
|
||||
return $t('flowCanvas.confirmLeave')
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
// 添加事件监听
|
||||
window.addEventListener('beforeunload', handleBeforeUnload)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload)
|
||||
})
|
||||
defineExpose({
|
||||
open,
|
||||
close,
|
||||
|
||||
@@ -54,6 +54,10 @@ export class GenerateManager {
|
||||
nodeDataItem.url = item.url
|
||||
nodeDataItem.createTime = item.createTime
|
||||
nodeDataItem.status = item.status
|
||||
if(item.glbPath){
|
||||
nodeDataItem.glbPath = item.glbPath
|
||||
nodeDataItem.glbInfoObj = item.glbInfoObj
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<div class="modal-box">
|
||||
<slot v-if="currentUrl" :currentUrl="currentUrl"></slot>
|
||||
<slot v-if="currentData" :currentData="currentData"></slot>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
@@ -27,14 +27,14 @@
|
||||
}
|
||||
})
|
||||
const showDialog = ref(false)
|
||||
let currentUrl = ref('')
|
||||
const open = (url_: any) => {
|
||||
currentUrl.value = url_
|
||||
let currentData = ref(null)
|
||||
const open = (data: any,) => {
|
||||
currentData.value = data
|
||||
showDialog.value = true
|
||||
}
|
||||
const close = () => {
|
||||
showDialog.value = false
|
||||
currentUrl.value = ''
|
||||
currentData.value = null
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import JSZip from 'jszip'
|
||||
export const createId = (before: string = 'node') => {
|
||||
const time = Date.now().toString(36)
|
||||
const random = Math.random().toString(36).substring(2, 20)
|
||||
@@ -15,4 +16,47 @@ export const downloadImage = (url: string, name: string) => {
|
||||
a.download = name || 'image.png'
|
||||
a.click()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** 批量下载图片 */
|
||||
export const downImgListToZip = async (imagesParams) => {
|
||||
const zip = new JSZip()
|
||||
const promises = []
|
||||
// 遍历下载每个图片
|
||||
imagesParams.forEach((img, index) => {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.open('GET', img.url, true)
|
||||
xhr.responseType = "blob";
|
||||
xhr.onload = () => {
|
||||
if (xhr.status === 200) {
|
||||
const fileName = img.name
|
||||
zip.file(fileName, xhr.response)
|
||||
resolve('')
|
||||
} else {
|
||||
reject(new Error(`下载失败: ${img.url}`))
|
||||
}
|
||||
}
|
||||
|
||||
xhr.onerror = () => reject(new Error(`网络错误: ${img.url}`))
|
||||
xhr.send()
|
||||
})
|
||||
|
||||
promises.push(promise)
|
||||
console.log(promises,zip)
|
||||
})
|
||||
|
||||
// 等待所有图片下载完成
|
||||
Promise.all(promises)
|
||||
.then(() => zip.generateAsync({ type: 'blob' }))
|
||||
.then((content) => {
|
||||
// 下载zip
|
||||
console.log(content)
|
||||
const link = document.createElement('a')
|
||||
link.href = URL.createObjectURL(content)
|
||||
link.download = 'DesignFiles'
|
||||
link.click()
|
||||
URL.revokeObjectURL(link.href)
|
||||
})
|
||||
.catch((error) => console.error('下载失败:', error))
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
popper-style="width:auto; min-width: 22rem; padding: 0.8rem; border-radius: 1.4rem;"
|
||||
>
|
||||
<template #reference>
|
||||
<img class="pic" src="@/assets/images/pic.jpg" />
|
||||
<img class="pic" :src="userInfoStore.state.userInfo?.avatar || '@/assets/images/pic.jpg'" />
|
||||
</template>
|
||||
<div class="menu-box">
|
||||
<div>
|
||||
@@ -97,6 +97,7 @@
|
||||
width: 4.65rem;
|
||||
height: 4.65rem;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
.menu-box {
|
||||
|
||||
@@ -81,6 +81,7 @@ export default {
|
||||
userAgreement: 'User Agreement',
|
||||
privacyPolicy: 'Privacy Policy',
|
||||
view: 'View',
|
||||
remainingNum: 'Remaining number of times to upload profile picture:',
|
||||
},
|
||||
Country:{
|
||||
unitedStates: 'United States',
|
||||
@@ -185,6 +186,7 @@ export default {
|
||||
deleteCardConfirm: 'Are you sure you want to delete this function card?',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
confirmLeave: 'Are you sure you want to leave? You may have unsaved changes.',
|
||||
},
|
||||
assistant: {
|
||||
inputPlaceholder: 'Ask anything',
|
||||
|
||||
@@ -81,7 +81,8 @@ export default {
|
||||
timesPerHour: '{time} 次/小时',
|
||||
userAgreement: '用户协议',
|
||||
privacyPolicy: '隐私政策',
|
||||
view: '查看'
|
||||
view: '查看',
|
||||
remainingNum: '剩余上传头像次数:',
|
||||
},
|
||||
Country: {
|
||||
unitedStates: '美国',
|
||||
@@ -179,7 +180,8 @@ export default {
|
||||
flowCanvas: {
|
||||
deleteCardConfirm: '确定要删除该功能卡片吗?',
|
||||
confirm: '确认',
|
||||
cancel: '取消'
|
||||
cancel: '取消',
|
||||
confirmLeave: '您可能有未保存的更改,确定要离开吗?',
|
||||
},
|
||||
assistant: {
|
||||
inputPlaceholder: '请输入'
|
||||
|
||||
@@ -21,6 +21,10 @@ export const useUserInfoStore = defineStore('userInfo', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const updateUserInfo = (data: any) => {
|
||||
state.value.userInfo = { ...state.value.userInfo, ...data }
|
||||
}
|
||||
|
||||
// actions
|
||||
const setUserInfo = (data: any) => {
|
||||
state.value.userInfo = data
|
||||
@@ -49,6 +53,7 @@ export const useUserInfoStore = defineStore('userInfo', () => {
|
||||
getUserInfo,
|
||||
setToken,
|
||||
setUserInfo,
|
||||
logOut
|
||||
logOut,
|
||||
updateUserInfo
|
||||
}
|
||||
})
|
||||
|
||||
@@ -78,13 +78,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-item" @click="onCanvas(false)">
|
||||
<span class="title">画布</span>
|
||||
</div>
|
||||
<div class="menu-item" @click="onCanvas(true)">
|
||||
<span class="title">深度画布</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -111,12 +104,6 @@
|
||||
const onCreateProject = () => {
|
||||
router.push({ name: 'mainInput' })
|
||||
}
|
||||
const onHome = () => {}
|
||||
const onCanvas = (depth: boolean) => {
|
||||
const query = {}
|
||||
if (depth) query['depth'] = '1'
|
||||
router.push({ name: 'test', query })
|
||||
}
|
||||
const onHistory = () => {
|
||||
if (isCollapse.value) {
|
||||
globalStore.setHomeLeftNavCollapse(false)
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="label">{{ $t('Home.userProfile') }}</div>
|
||||
<div class="profile">
|
||||
<img :src="userInfo?.profile" />
|
||||
<span class="edit" @click="onProfileEdit">
|
||||
<svg-icon name="profile-edit" size="11" />
|
||||
</span>
|
||||
</div>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="`${$t('Home.remainingNum')} ${remainingNum}`"
|
||||
placement="top"
|
||||
>
|
||||
<div class="profile">
|
||||
<img :src="userInfo?.avatar" />
|
||||
<span class="edit" @click="onProfileEdit">
|
||||
<svg-icon name="profile-edit" size="11" />
|
||||
</span>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label">{{ $t('Home.userName') }}</div>
|
||||
@@ -27,11 +34,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, nextTick, inject } from 'vue'
|
||||
import { computed, ref, nextTick, inject, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import dropdownMenu from '@/components/dropdown-menu.vue'
|
||||
import { useUserInfoStore } from '@/stores'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { uploadImage } from '@/api/upload'
|
||||
import { UpdateUserAvatar, getAvatarLimit } from '@/api/user'
|
||||
const router = useRouter()
|
||||
const { locale } = useI18n()
|
||||
const userInfoStore = useUserInfoStore()
|
||||
@@ -40,6 +49,7 @@
|
||||
{ label: 'English', value: 'ENGLISH' },
|
||||
{ label: '中文', value: 'CHINESE_SIMPLIFIED' }
|
||||
])
|
||||
const remainingNum = ref(0)
|
||||
const changeLang = (value: string) => {
|
||||
locale.value = value
|
||||
localStorage.setItem('language', value)
|
||||
@@ -50,10 +60,32 @@
|
||||
const logout = () => {
|
||||
userInfoStore.logOut()
|
||||
}
|
||||
const getAvatarLimitNum = ()=>{
|
||||
getAvatarLimit().then((res:any) => {
|
||||
if (res) {
|
||||
remainingNum.value = res
|
||||
}
|
||||
})
|
||||
}
|
||||
const onProfileEdit = () => {
|
||||
// MyEvent.emit('openProfileEditDialog')
|
||||
console.log('openProfileEditDialog')
|
||||
const input = document.createElement('input')
|
||||
input.type = 'file'
|
||||
input.accept = 'image/png, image/jpeg, image/jpg'
|
||||
input.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0]
|
||||
const formData = new FormData()
|
||||
formData.append('avatar', file)
|
||||
UpdateUserAvatar(formData).then((res) => {
|
||||
userInfoStore.updateUserInfo({avatar: res})
|
||||
getAvatarLimitNum()
|
||||
})
|
||||
})
|
||||
input.click()
|
||||
}
|
||||
onMounted(()=>{
|
||||
getAvatarLimitNum()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@@ -75,6 +107,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
> .edit {
|
||||
position: absolute;
|
||||
|
||||