Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/FiDA_Front
1885
package-lock.json
generated
@@ -19,6 +19,7 @@
|
||||
"crypto-js": "^4.2.0",
|
||||
"dagre": "^0.8.5",
|
||||
"element-plus": "^2.13.2",
|
||||
"fabric-with-all": "^5.3.1",
|
||||
"gsap": "^3.13.0",
|
||||
"markdown-it": "^14.1.0",
|
||||
"md5": "^2.3.0",
|
||||
@@ -27,6 +28,7 @@
|
||||
"pinia-persistedstate-plugin": "^0.1.0",
|
||||
"pinia-plugin-persistedstate": "^3.1.0",
|
||||
"vue": "^3.2.47",
|
||||
"vue-draggable-plus": "^0.6.1",
|
||||
"vue-i18n": "^11.2.8",
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
|
||||
4
src/assets/icons/dc/brush.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.24074 0C3.46143 0 3.65143 0.155768 3.69471 0.372169C3.82112 1.00422 4.0682 1.54443 4.44805 1.95349C4.82378 2.35813 5.35318 2.6605 6.09463 2.78408C6.31786 2.82128 6.48148 3.01443 6.48148 3.24074C6.48148 3.46705 6.31786 3.6602 6.09463 3.6974C5.35318 3.82098 4.82378 4.12336 4.44805 4.52799C4.0682 4.93706 3.82112 5.47726 3.69471 6.10931C3.65143 6.32571 3.46143 6.48148 3.24074 6.48148C3.02005 6.48148 2.83005 6.32571 2.78677 6.10931C2.66036 5.47726 2.41328 4.93706 2.03343 4.52799C1.6577 4.12336 1.1283 3.82098 0.386852 3.6974C0.163617 3.6602 0 3.46705 0 3.24074C0 3.01443 0.163617 2.82128 0.386852 2.78408C1.1283 2.6605 1.6577 2.35813 2.03343 1.95349C2.41328 1.54443 2.66036 1.00422 2.78677 0.372169C2.83005 0.155768 3.02005 0 3.24074 0Z" fill="#0D0D0D"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3823 2.58591C13.0223 0.945877 15.6813 0.945875 17.3214 2.58591C18.9614 4.22594 18.9614 6.88496 17.3214 8.525L10.2733 15.5731C9.83003 16.0166 9.52299 16.3237 9.16518 16.5682C8.84863 16.7844 8.50644 16.9605 8.14648 17.0924C7.73958 17.2415 7.3112 17.3128 6.69266 17.4158L3.95291 17.8724L3.9263 17.8768C3.77758 17.9016 3.61119 17.9294 3.46669 17.9403C3.30911 17.9522 3.05843 17.9564 2.7935 17.8427C2.466 17.7023 2.20502 17.4413 2.06455 17.1138C1.95092 16.8489 1.95506 16.5982 1.96695 16.4406C1.97786 16.2961 2.00565 16.1297 2.03049 15.981L2.49154 13.2146C2.59448 12.5961 2.66578 12.1677 2.81487 11.7608C2.94676 11.4008 3.12286 11.0587 3.33911 10.7421C3.58355 10.3843 3.8907 10.0773 4.33417 9.63402L11.3823 2.58591ZM16.0119 3.89537C15.0951 2.97853 13.6086 2.97853 12.6917 3.89537L12.4205 4.16661L15.7407 7.48678L16.0119 7.21554C16.9288 6.2987 16.9288 4.81221 16.0119 3.89537ZM5.70125 10.8859L11.1111 5.47606L14.4312 8.79624L9.02142 14.206C8.49891 14.7285 8.31816 14.9041 8.12057 15.0391C7.93064 15.1688 7.72533 15.2745 7.50936 15.3536C7.28468 15.4359 7.03676 15.481 6.30788 15.6025L3.9042 16.0031L4.30481 13.5994C4.42629 12.8705 4.47135 12.6226 4.55367 12.3979C4.63281 12.182 4.73846 11.9766 4.86821 11.7867C5.00319 11.5891 5.17874 11.4084 5.70125 10.8859Z" fill="#0D0D0D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
5
src/assets/icons/dc/delete.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.33333 6C5.33333 5.63181 5.03486 5.33333 4.66667 5.33333C4.29848 5.33333 4 5.63181 4 6V9.33333C4 9.70152 4.29848 10 4.66667 10C5.03486 10 5.33333 9.70152 5.33333 9.33333V6Z" fill="#0D0D0D"/>
|
||||
<path d="M8 6C8 5.63181 7.70152 5.33333 7.33333 5.33333C6.96514 5.33333 6.66667 5.63181 6.66667 6V9.33333C6.66667 9.70152 6.96514 10 7.33333 10C7.70152 10 8 9.70152 8 9.33333V6Z" fill="#0D0D0D"/>
|
||||
<path d="M6.43845 0C7.66209 0 8.72872 0.832793 9.02549 2.0199L9.18718 2.66667H10.6572C10.663 2.66659 10.6687 2.66659 10.6745 2.66667H11.3333C11.7015 2.66667 12 2.96514 12 3.33333C12 3.70152 11.7015 4 11.3333 4H11.2657L10.521 10.9508C10.3758 12.3058 9.2323 13.3333 7.86951 13.3333H4.13049C2.7677 13.3333 1.62419 12.3058 1.479 10.9508L0.73428 4H0.666667C0.298477 4 0 3.70152 0 3.33333C0 2.96514 0.298477 2.66667 0.666667 2.66667H1.32552C1.33129 2.66659 1.33704 2.66659 1.34277 2.66667H2.81282L2.97451 2.0199C3.27128 0.832792 4.33791 0 5.56155 0H6.43845ZM8.68061 4C8.67095 4.00021 8.66132 4.00021 8.65172 4H3.34828C3.33868 4.00021 3.32905 4.00021 3.31939 4H2.07524L2.80475 10.8087C2.87734 11.4862 3.4491 12 4.13049 12H7.86951C8.5509 12 9.12266 11.4862 9.19525 10.8087L9.92476 4H8.68061ZM4.26802 2.34328L4.18717 2.66667H7.81281L7.73196 2.34329C7.58357 1.74973 7.05026 1.33333 6.43844 1.33333H5.56154C4.94972 1.33333 4.41641 1.74973 4.26802 2.34328Z" fill="#0D0D0D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
3
src/assets/icons/dc/details_edit.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.5532 0.206891C14.2974 -0.178709 15.1777 -0.00490283 15.7415 0.558867C16.3052 1.12264 16.479 2.00294 16.0934 2.74717C15.3784 4.12729 14.5304 5.33622 13.5101 6.42308C14.5359 7.69873 15.3069 9.0202 15.6325 10.2637C16.012 11.713 15.7992 13.1741 14.5421 14.1798C13.5324 14.9876 12.3977 14.8786 11.4916 14.5043C10.6096 14.1399 9.82734 13.4883 9.29034 12.9696C8.95931 12.6499 8.95016 12.1223 9.2699 11.7913C9.58965 11.4602 10.1172 11.4511 10.4482 11.7708C10.9319 12.238 11.5345 12.7187 12.1279 12.9638C12.6971 13.199 13.1283 13.1765 13.5009 12.8784C14.0891 12.4078 14.2897 11.7153 14.0202 10.686C13.7828 9.77925 13.1921 8.70984 12.3034 7.58311C11.8195 8.00397 11.3027 8.40655 10.75 8.79447C10.7255 8.81169 10.7004 8.8274 10.6748 8.84162C10.6231 9.44768 10.4192 9.96629 10.0602 10.3796C9.65159 10.8502 9.11272 11.1024 8.59774 11.2428C7.77682 11.4665 6.7942 11.4522 6.07721 11.4417C5.93922 11.4397 5.81108 11.4378 5.69582 11.4378C5.23558 11.4378 4.86248 11.0648 4.86248 10.6045C4.86248 10.4893 4.86062 10.3611 4.85861 10.2231C4.84816 9.50613 4.83384 8.52351 5.05756 7.70259C5.19791 7.18761 5.45015 6.64874 5.9207 6.24009C6.33404 5.88112 6.85265 5.67723 7.45872 5.62557C7.47294 5.59995 7.48865 5.57482 7.50586 5.55029C7.83713 5.07838 8.17908 4.63259 8.53398 4.21111C6.12523 2.88904 3.80895 2.99714 2.6076 4.19849C1.80113 5.00496 1.48263 6.27808 1.76866 7.80359C2.05381 9.32435 2.93278 11.0055 4.37536 12.4481C5.22706 13.2998 6.14731 13.8945 6.90004 14.2746C7.27598 14.4643 7.60327 14.5971 7.85067 14.6804C8.03589 14.7428 8.14203 14.7658 8.18099 14.7742C8.20161 14.7787 8.20341 14.7791 8.18816 14.7791C8.6484 14.7791 9.02149 15.1522 9.02148 15.6124C9.02147 16.0727 8.64837 16.4458 8.18813 16.4458C7.93574 16.4458 7.61085 16.3583 7.31864 16.2599C6.9906 16.1494 6.59022 15.9852 6.14893 15.7624C5.26719 15.3172 4.19474 14.6245 3.19685 13.6266C1.54779 11.9775 0.485207 10.0023 0.130543 8.11074C-0.223229 6.22396 0.120209 4.32886 1.42909 3.01998C3.47358 0.975487 6.86496 1.27269 9.69961 2.95954C10.8321 1.86325 12.0974 0.961153 13.5532 0.206891ZM9.23584 6.00456C9.67471 6.25943 10.0409 6.62563 10.2958 7.06449C12.1825 5.64349 13.5551 4.02343 14.6136 1.98044C14.6465 1.91697 14.6424 1.81685 14.563 1.73738C14.4835 1.65791 14.3834 1.65384 14.3199 1.68672C12.2769 2.74523 10.6568 4.11787 9.23584 6.00456ZM6.52247 9.77786C7.11758 9.78101 7.6793 9.76562 8.1595 9.63475C8.48308 9.54657 8.68219 9.42461 8.80188 9.28679C8.91025 9.16201 9.02126 8.94714 9.02126 8.52513C9.02126 7.83695 8.46338 7.27908 7.7752 7.27908C7.35319 7.27908 7.13832 7.39008 7.01354 7.49845C6.87572 7.61814 6.75377 7.81726 6.66558 8.14083C6.53471 8.62103 6.51932 9.18276 6.52247 9.77786Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
3
src/assets/icons/dc/down_arrow.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="11" height="6" viewBox="0 0 11 6" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.195262 0.195262C0.455612 -0.0650874 0.877722 -0.0650874 1.13807 0.195262L5.33333 4.39052L9.5286 0.195263C9.78895 -0.065087 10.2111 -0.0650869 10.4714 0.195263C10.7318 0.455612 10.7318 0.877722 10.4714 1.13807L5.80474 5.80474C5.54439 6.06509 5.12228 6.06509 4.86193 5.80474L0.195262 1.13807C-0.0650874 0.877722 -0.0650874 0.455612 0.195262 0.195262Z" fill="#0D0D0D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 480 B |
3
src/assets/icons/dc/down_arrow2.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 1L5 5L9 1" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 205 B |
4
src/assets/icons/dc/drag.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="10" height="18" viewBox="0 0 10 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 18C0.89543 18 0 17.1046 0 16C0 14.8954 0.89543 14 2 14C3.10457 14 4 14.8954 4 16C4 17.1046 3.10457 18 2 18ZM2 11C0.89543 11 0 10.1046 0 9C0 7.89543 0.89543 7 2 7C3.10457 7 4 7.89543 4 9C4 10.1046 3.10457 11 2 11ZM2 4C0.89543 4 0 3.10457 0 2C0 0.895432 0.89543 0 2 0C3.10457 0 4 0.89543 4 2C4 3.10457 3.10457 4 2 4Z" fill="#C9C9C9"/>
|
||||
<path d="M8 18C6.89543 18 6 17.1046 6 16C6 14.8954 6.89543 14 8 14C9.10457 14 10 14.8954 10 16C10 17.1046 9.10457 18 8 18ZM8 11C6.89543 11 6 10.1046 6 9C6 7.89543 6.89543 7 8 7C9.10457 7 10 7.89543 10 9C10 10.1046 9.10457 11 8 11ZM8 4C6.89543 4 6 3.10457 6 2C6 0.895432 6.89543 0 8 0C9.10457 0 10 0.89543 10 2C10 3.10457 9.10457 4 8 4Z" fill="#C9C9C9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 802 B |
3
src/assets/icons/dc/eraser.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.3137 4.91977C18.4501 3.8003 18.4569 1.96944 17.329 0.841498C16.1991 -0.288403 14.3643 -0.279274 13.2457 0.861819L10.7437 3.41414L10.7066 3.37707C9.48479 2.15647 7.56609 2.34904 6.45155 3.47723C5.10582 4.83945 3.61847 5.86054 1.86914 6.61961C0.386492 7.26297 -0.563044 9.10374 0.371879 10.7126C0.671202 11.2277 0.975937 11.7181 1.28942 12.1864C1.52389 12.5368 1.96541 12.6845 2.36351 12.5458L2.57062 12.4737L2.40435 12.756C2.20816 13.0891 2.23956 13.509 2.48312 13.8092C3.27794 14.789 3.91787 15.4453 4.68625 16.0374C5.4368 16.6158 6.29003 17.1161 7.46188 17.7957C9.07008 18.7284 10.9124 17.7826 11.5571 16.3C12.3168 14.5526 13.3388 13.0668 14.7024 11.7225C15.832 10.6087 16.0251 8.69029 14.8026 7.46902L14.7646 7.43099L17.3137 4.91977ZM14.5682 2.15818C14.9658 1.75259 15.6179 1.74934 16.0195 2.15096C16.4204 2.55187 16.418 3.20264 16.0141 3.60054L12.8001 6.76677C12.6242 6.94001 12.5248 7.1763 12.5239 7.42315C12.5231 7.67 12.6208 7.90698 12.7955 8.08144L13.4938 8.77913C13.8907 9.17561 13.9026 9.91047 13.4022 10.4037C13.223 10.5804 13.0489 10.7595 12.8798 10.9411L7.23237 5.29936C7.41371 5.13081 7.59252 4.95729 7.76895 4.7787C8.26307 4.27852 9.00027 4.29004 9.39781 4.68718L10.0962 5.38487C10.271 5.5595 10.5083 5.65702 10.7554 5.65573C11.0025 5.65444 11.2388 5.55444 11.4118 5.378L14.5682 2.15818ZM5.80124 6.48726C4.81926 7.21137 3.76176 7.81704 2.60629 8.31843C1.90885 8.62106 1.71462 9.33751 1.97302 9.78217C2.12802 10.0489 2.2838 10.3073 2.44087 10.5579L4.3446 9.8947C4.71137 9.76693 5.11889 9.88163 5.36517 10.1819C5.61144 10.4823 5.64413 10.9043 5.44703 11.239L4.33112 13.1336C4.87159 13.7602 5.31808 14.1864 5.81659 14.5706C6.45325 15.0612 7.19583 15.5007 8.39094 16.1938C8.8383 16.4533 9.55647 16.2569 9.85878 15.5616C10.3605 14.4077 10.9665 13.3516 11.6908 12.371L5.80124 6.48726Z" fill="#0D0D0D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
3
src/assets/icons/dc/image.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.33213 3.99343e-07H11.3345C12.0799 -1.14112e-05 12.695 -2.11238e-05 13.1961 0.0409209C13.7166 0.0834442 14.195 0.174707 14.6444 0.40368C15.3413 0.758767 15.9079 1.32536 16.263 2.02226C16.492 2.47164 16.5832 2.95007 16.6257 3.47053C16.6667 3.97163 16.6667 4.58678 16.6667 5.33211V11.3346C16.6667 12.0799 16.6667 12.695 16.6257 13.1961C16.5832 13.7166 16.492 14.195 16.263 14.6444C15.9079 15.3413 15.3413 15.9079 14.6444 16.263C14.195 16.492 13.7166 16.5832 13.1961 16.6257C12.695 16.6667 12.0799 16.6667 11.3346 16.6667H5.33211C4.58678 16.6667 3.97163 16.6667 3.47053 16.6257C2.95007 16.5832 2.47164 16.492 2.02226 16.263C1.32536 15.9079 0.758767 15.3413 0.40368 14.6444C0.174707 14.195 0.0834442 13.7166 0.0409209 13.1961C-2.11238e-05 12.695 -1.14112e-05 12.0799 3.99343e-07 11.3345V5.33213C-1.14112e-05 4.58679 -2.11238e-05 3.97164 0.0409209 3.47053C0.0834442 2.95007 0.174707 2.47164 0.40368 2.02226C0.758767 1.32536 1.32536 0.758767 2.02226 0.40368C2.47164 0.174707 2.95007 0.0834442 3.47053 0.0409209C3.97164 -2.11238e-05 4.58679 -1.14112e-05 5.33213 3.99343e-07ZM1.85185 9.25926V11.2963C1.85185 12.0894 1.85257 12.6286 1.88662 13.0453C1.91979 13.4513 1.97991 13.6589 2.05369 13.8037C2.23124 14.1521 2.51453 14.4354 2.86298 14.613C3.0078 14.6868 3.21539 14.7469 3.62133 14.78C4.03808 14.8141 4.57724 14.8148 5.37037 14.8148H11.2963C12.0894 14.8148 12.6286 14.8141 13.0453 14.78C13.4513 14.7469 13.6589 14.6868 13.8037 14.613C14.1521 14.4354 14.4354 14.1521 14.613 13.8037C14.6868 13.6589 14.7469 13.4513 14.78 13.0453C14.8141 12.6286 14.8148 12.0894 14.8148 11.2963V9.25926H12.963C12.6639 9.25926 12.3833 9.11485 12.2095 8.87152L10.6481 6.68562L6.77198 12.1123C6.59817 12.3556 6.31755 12.5 6.01852 12.5C5.71949 12.5 5.43887 12.3556 5.26506 12.1123L3.22721 9.25926H1.85185ZM14.8148 7.40741V5.37037C14.8148 4.57724 14.8141 4.03808 14.78 3.62133C14.7469 3.21539 14.6868 3.0078 14.613 2.86298C14.4354 2.51453 14.1521 2.23124 13.8037 2.05369C13.6589 1.97991 13.4513 1.91979 13.0453 1.88662C12.6286 1.85257 12.0894 1.85185 11.2963 1.85185H5.37037C4.57724 1.85185 4.03808 1.85257 3.62133 1.88662C3.21539 1.91979 3.0078 1.97991 2.86298 2.05369C2.51453 2.23124 2.23124 2.51453 2.05369 2.86298C1.97991 3.0078 1.91979 3.21539 1.88662 3.62133C1.85257 4.03808 1.85185 4.57724 1.85185 5.37037V7.40741H3.7037C4.00273 7.40741 4.28335 7.55182 4.45716 7.79515L6.01852 9.98105L9.89469 4.55441C10.0685 4.31108 10.3491 4.16667 10.6481 4.16667C10.9472 4.16667 11.2278 4.31108 11.4016 4.55441L13.4395 7.40741H14.8148Z" fill="#0D0D0D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
9
src/assets/icons/dc/layer.svg
Normal file
|
After Width: | Height: | Size: 23 KiB |
3
src/assets/icons/dc/move.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="15" height="19" viewBox="0 0 15 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.6709 0.0615234C8.10609 0.0283563 8.55366 0.134081 8.89941 0.387695C9.23305 0.632466 9.46557 1.01335 9.50391 1.53125C10.351 1.35132 11.267 1.44554 11.7588 2.27734C11.8015 2.3493 11.8659 2.4826 11.9189 2.61523C11.9456 2.68187 11.9695 2.74999 11.9873 2.81055C12.0046 2.86956 12.0175 2.92657 12.0176 2.9707V3.5957C12.5446 3.4294 13.14 3.49012 13.624 3.73828C14.1314 3.99842 14.5203 4.46431 14.5742 5.08496C14.6215 5.63262 14.6169 6.23619 14.6025 6.84082C14.5882 7.44669 14.5638 8.05348 14.5703 8.6123C14.5869 9.99368 14.7346 11.4314 14.6328 12.7979C14.5306 14.1688 14.1779 15.482 13.1865 16.626C12.0543 17.9324 10.3202 18.602 8.55762 18.6631C6.7949 18.724 4.99508 18.1761 3.72949 17.04C1.92211 15.417 1.55066 13.6053 0.762695 11.4863C0.683586 11.2735 0.580869 11.0633 0.477539 10.8525C0.374729 10.6428 0.271227 10.4322 0.195312 10.2217C-0.173241 9.19784 0.228813 8.3138 0.931641 7.84082C1.60836 7.3855 2.5633 7.31169 3.37402 7.85449V3.34961H3.375C3.51944 2.06624 4.91534 1.38488 6.06836 1.80664C6.0365 1.28206 6.21192 0.868618 6.50293 0.575195C6.80823 0.26738 7.23566 0.0947109 7.6709 0.0615234ZM7.72461 1.09375C7.46487 1.08615 7.19561 1.25846 7.1543 1.625L7.15137 8.6123L7.13965 8.73438C7.09082 9.00322 6.89341 9.17338 6.67676 9.20898C6.55333 9.22924 6.42377 9.20583 6.31445 9.13184C6.20476 9.05759 6.11923 8.93515 6.0791 8.76465L6.07617 8.75195H6.07812L6.05176 3.38672C5.98373 3.13985 5.70675 2.9778 5.40723 2.95508C5.10822 2.93245 4.82595 3.05106 4.74219 3.32129V9.65234L5.86621 11.4463L5.87012 11.4531L5.87207 11.46C5.97956 11.8175 5.83236 12.1087 5.58691 12.2363C5.34248 12.3633 5.0111 12.3228 4.7627 12.0459C4.43376 11.6788 4.12868 11.1543 3.83105 10.624C3.53193 10.0911 3.24004 9.55123 2.93457 9.14258C2.75922 8.90799 2.52347 8.74466 2.2793 8.69531C2.03753 8.64653 1.78269 8.70852 1.55957 8.93164L1.55859 8.93262C1.51343 8.9778 1.45826 9.06092 1.41211 9.15332C1.36587 9.24592 1.33277 9.33916 1.3252 9.40137C1.32117 9.43477 1.32473 9.48179 1.33691 9.54199C1.34896 9.60148 1.36879 9.67074 1.39453 9.74707C1.44598 9.89961 1.51989 10.0773 1.60059 10.2588C1.76058 10.6185 1.94881 10.9985 2.02344 11.207C2.59378 12.8 2.9251 14.2378 4.05176 15.5625C5.42953 17.1827 7.62816 17.7419 9.54785 17.3232C11.4658 16.9049 13.0978 15.5124 13.3545 13.2334L13.3301 5.12207C13.2531 4.84033 12.9517 4.69225 12.6455 4.72461C12.3426 4.75666 12.0525 4.96612 12.0156 5.38379C11.9584 6.03179 11.9831 6.71982 12.0088 7.41406C12.0344 8.10699 12.0609 8.8062 12.0059 9.4707V9.47559H12.0049C11.9452 9.83123 11.6474 10.0102 11.3555 10.0137C11.0633 10.0171 10.763 9.84488 10.7012 9.48828L10.6992 9.47852H10.7002L10.6963 3.17578L10.665 3.01562C10.5699 2.66693 10.3323 2.48832 10.1123 2.47852C9.98617 2.47302 9.85974 2.52211 9.75879 2.63184C9.65734 2.74218 9.5789 2.91723 9.55762 3.16602L9.55664 8.99316V9.00098L9.55469 9.00977C9.44373 9.36044 9.13052 9.5024 8.84082 9.4668C8.55173 9.43119 8.27883 9.21782 8.24414 8.85645L8.24316 8.85059L8.24512 1.66797L8.23145 1.5332C8.2069 1.40874 8.15154 1.31244 8.08008 1.24219C7.98412 1.14786 7.85599 1.09762 7.72461 1.09375Z" fill="black" stroke="black" stroke-width="0.111111"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
3
src/assets/icons/dc/rectangle.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">
|
||||
<rect x="1" y="1" width="14" height="14" rx="2" stroke="#0D0D0D" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 187 B |
3
src/assets/icons/dc/redo.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="23" height="23" viewBox="0 0 23 23" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.16 2.12276C13.7984 2.48436 13.7984 3.07062 14.16 3.43222L16.283 5.55527H9.25915C5.67953 5.55527 2.77767 8.45712 2.77767 12.0367C2.77767 15.6164 5.67953 18.5182 9.25915 18.5182H12.9629C13.4742 18.5182 13.8888 18.1037 13.8888 17.5923C13.8888 17.0809 13.4742 16.6664 12.9629 16.6664H9.25915C6.70228 16.6664 4.62952 14.5936 4.62952 12.0367C4.62952 9.47987 6.70228 7.40712 9.25915 7.40712H16.283L14.16 9.53017C13.7984 9.89176 13.7984 10.478 14.16 10.8396C14.5216 11.2012 15.1078 11.2012 15.4694 10.8396L19.1731 7.13592C19.5347 6.77432 19.5347 6.18806 19.1731 5.82646L15.4694 2.12276C15.1078 1.76116 14.5216 1.76116 14.16 2.12276Z" fill="#0D0D0D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 759 B |
3
src/assets/icons/dc/select.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.20508 0.851562C0.985291 0.774157 0.7742 0.985289 0.851562 1.20508L5.83887 15.3369C5.92158 15.5713 6.24738 15.5874 6.35254 15.3623L8.91992 9.85938C9.11305 9.44566 9.44564 9.11303 9.85938 8.91992L15.3623 6.35254C15.5873 6.24733 15.5712 5.92157 15.3369 5.83887L1.20508 0.851562Z" stroke="#0D0D0D" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 481 B |
3
src/assets/icons/dc/selectbox.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.70215 0.0996094C4.2287 0.0997879 4.65505 0.526194 4.65527 1.05273C4.65527 1.46912 4.99377 1.80664 5.41016 1.80664H10.9902C11.4066 1.80664 11.7451 1.46912 11.7451 1.05273C11.7453 0.526194 12.1717 0.0997877 12.6982 0.0996094H15.2002C15.8077 0.0996094 16.2998 0.591706 16.2998 1.19922V4.375C16.2998 4.46153 16.2659 4.54492 16.2051 4.60645C16.1434 4.66869 16.0593 4.704 15.9717 4.7041H15.4229C14.9259 4.7041 14.5226 5.10655 14.5225 5.60352V11.6475C14.5225 11.6798 14.5363 11.7095 14.5586 11.7324C14.5594 11.7316 14.5607 11.7313 14.5615 11.7305C14.5682 11.7242 14.598 11.6944 14.6445 11.6943H15.9717C16.0593 11.6944 16.1434 11.7297 16.2051 11.792C16.2659 11.8535 16.2998 11.9369 16.2998 12.0234V15.1992C16.2998 15.8067 15.8077 16.2988 15.2002 16.2988H12.6982C12.1717 16.2987 11.7453 15.8722 11.7451 15.3457C11.7451 14.9293 11.4066 14.5918 10.9902 14.5918H5.41016C4.99377 14.5918 4.65527 14.9293 4.65527 15.3457C4.65505 15.8722 4.2287 16.2987 3.70215 16.2988H1.2002C0.592682 16.2988 0.100586 15.8067 0.100586 15.1992V12.0234C0.100634 11.9369 0.134469 11.8535 0.195312 11.792C0.256985 11.7297 0.341088 11.6944 0.428711 11.6943H1.75586L1.80762 11.708L1.82422 11.7148C1.82637 11.713 1.83251 11.7065 1.84082 11.6895C1.86454 11.6407 1.87792 11.5566 1.87793 11.4707V4.75098C1.87793 4.71821 1.86377 4.68808 1.84082 4.66504C1.84 4.66585 1.83971 4.66718 1.83887 4.66797C1.83223 4.67421 1.80239 4.70406 1.75586 4.7041H0.428711C0.341088 4.704 0.256985 4.66869 0.195312 4.60645C0.134468 4.54492 0.100634 4.46153 0.100586 4.375V1.19922C0.100586 0.591706 0.592682 0.0996094 1.2002 0.0996094H3.70215ZM2.08887 12.7939C1.59199 12.7941 1.18958 13.1965 1.18945 13.6934V14.3008C1.18969 14.7976 1.59206 15.2001 2.08887 15.2002H2.66699C3.16384 15.2001 3.56617 14.7976 3.56641 14.3008V13.6934C3.56628 13.1965 3.16391 12.794 2.66699 12.7939H2.08887ZM13.7334 12.7939C13.2365 12.794 12.8341 13.1965 12.834 13.6934V14.3008C12.8342 14.7976 13.2365 15.2001 13.7334 15.2002H14.3115C14.8083 15.2001 15.2107 14.7976 15.2109 14.3008V13.6934C15.2108 13.1965 14.8084 12.7941 14.3115 12.7939H13.7334ZM4.78418 2.90625C4.74997 2.90637 4.71745 2.92004 4.69336 2.94434C4.6695 2.96847 4.65527 3.00122 4.65527 3.03516V3.75879C4.65527 4.28039 4.23254 4.7041 3.71094 4.7041C3.29996 4.7043 2.9668 5.03722 2.9668 5.44824V10.9502C2.9668 11.3612 3.29996 11.6941 3.71094 11.6943C4.23254 11.6943 4.65527 12.118 4.65527 12.6396V13.3633C4.65527 13.3972 4.6695 13.43 4.69336 13.4541C4.71745 13.4784 4.74997 13.4921 4.78418 13.4922H11.6162C11.6504 13.4921 11.6829 13.4784 11.707 13.4541C11.7309 13.43 11.7451 13.3972 11.7451 13.3633V12.6396C11.7451 12.118 12.1679 11.6943 12.6895 11.6943C13.1004 11.6941 13.4336 11.3612 13.4336 10.9502V5.44824C13.4336 5.03722 13.1004 4.7043 12.6895 4.7041C12.1679 4.7041 11.7451 4.28039 11.7451 3.75879V3.03516C11.7451 3.00122 11.7309 2.96847 11.707 2.94434C11.6829 2.92004 11.6504 2.90637 11.6162 2.90625H4.78418ZM2.08887 1.19824C1.59206 1.19836 1.18969 1.60087 1.18945 2.09766V2.70508C1.18958 3.20195 1.59199 3.60437 2.08887 3.60449H2.66699C3.16391 3.60442 3.56628 3.20198 3.56641 2.70508V2.09766C3.56617 1.60085 3.16384 1.19831 2.66699 1.19824H2.08887ZM13.7334 1.19824C13.2365 1.19831 12.8342 1.60085 12.834 2.09766V2.70508C12.8341 3.20198 13.2365 3.60442 13.7334 3.60449H14.3115C14.8084 3.60437 15.2108 3.20195 15.2109 2.70508V2.09766C15.2107 1.60087 14.8083 1.19836 14.3115 1.19824H13.7334Z" fill="black" stroke="black" stroke-width="0.2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
3
src/assets/icons/dc/show.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="12" viewBox="0 0 16 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.48161 3.0607C2.43325 4.0275 1.73109 5.16098 1.38567 5.79807C1.31589 5.92677 1.31589 6.07323 1.38567 6.20193C1.73109 6.83903 2.43325 7.9725 3.48161 8.9393C4.52662 9.903 5.8758 10.6667 7.53865 10.6667C9.2015 10.6667 10.5507 9.903 11.5957 8.9393C12.644 7.9725 13.3462 6.83903 13.6916 6.20193C13.7614 6.07323 13.7614 5.92677 13.6916 5.79807C13.3462 5.16097 12.644 4.0275 11.5957 3.0607C10.5507 2.097 9.2015 1.33333 7.53865 1.33333C5.8758 1.33333 4.52662 2.097 3.48161 3.0607ZM2.57771 2.08054C3.77905 0.972662 5.43586 0 7.53865 0C9.64144 0 11.2983 0.972662 12.4996 2.08054C13.6976 3.18532 14.4821 4.45866 14.8638 5.16256C15.1485 5.68768 15.1485 6.31232 14.8638 6.83744C14.4821 7.54134 13.6976 8.81468 12.4996 9.91946C11.2983 11.0273 9.64144 12 7.53865 12C5.43586 12 3.77905 11.0273 2.57771 9.91946C1.37972 8.81468 0.595173 7.54134 0.213535 6.83744C-0.071178 6.31232 -0.0711783 5.68768 0.213534 5.16256C0.595173 4.45866 1.37971 3.18532 2.57771 2.08054ZM7.53865 4.33333C6.61817 4.33333 5.87198 5.07953 5.87198 6C5.87198 6.92047 6.61817 7.66667 7.53865 7.66667C8.45912 7.66667 9.20532 6.92047 9.20532 6C9.20532 5.07953 8.45912 4.33333 7.53865 4.33333ZM4.53865 6C4.53865 4.34315 5.88179 3 7.53865 3C9.1955 3 10.5386 4.34315 10.5386 6C10.5386 7.65685 9.1955 9 7.53865 9C5.88179 9 4.53865 7.65685 4.53865 6Z" fill="#0D0D0D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
3
src/assets/icons/dc/undo.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="23" height="23" viewBox="0 0 23 23" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.06219 2.12276C8.42379 2.48436 8.42379 3.07062 8.06219 3.43222L5.93914 5.55527H12.963C16.5426 5.55527 19.4445 8.45712 19.4445 12.0367C19.4445 15.6164 16.5426 18.5182 12.963 18.5182H9.25931C8.74794 18.5182 8.33339 18.1037 8.33339 17.5923C8.33339 17.0809 8.74794 16.6664 9.25931 16.6664H12.963C15.5199 16.6664 17.5926 14.5936 17.5926 12.0367C17.5926 9.47987 15.5199 7.40712 12.963 7.40712H5.93914L8.06219 9.53017C8.42379 9.89176 8.42379 10.478 8.06219 10.8396C7.70059 11.2012 7.11433 11.2012 6.75273 10.8396L3.04903 7.13592C2.68743 6.77432 2.68743 6.18806 3.04903 5.82646L6.75273 2.12276C7.11433 1.76116 7.70059 1.76116 8.06219 2.12276Z" fill="#0D0D0D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 767 B |
9
src/assets/icons/dc/workbench.svg
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
3
src/assets/icons/profile-edit.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="M7.74398 0.558645C8.48884 -0.186215 9.69649 -0.186215 10.4414 0.558645C11.1862 1.30351 11.1862 2.51116 10.4414 3.25602L6.81334 6.88404C6.34588 7.3515 5.73942 7.65473 5.08498 7.74822L3.75282 7.93853C3.56241 7.96573 3.37031 7.9017 3.23431 7.7657C3.09831 7.62969 3.03427 7.43759 3.06147 7.24719L3.25178 5.91502C3.34527 5.26058 3.6485 4.65412 4.11596 4.18667L7.74398 0.558645ZM9.57718 1.42282C9.30959 1.15524 8.87574 1.15524 8.60816 1.42282L4.98014 5.05084C4.69967 5.33132 4.51773 5.6952 4.46163 6.08786L4.38655 6.61346L4.91215 6.53837C5.30481 6.48228 5.66869 6.30034 5.94916 6.01987L9.57718 2.39185C9.84476 2.12426 9.84476 1.69041 9.57718 1.42282ZM4.88853 0.611451C4.88879 0.948934 4.61542 1.22273 4.27794 1.22299C3.66778 1.22347 3.23724 1.22801 2.90043 1.26001C2.57066 1.29134 2.37392 1.34588 2.22308 1.42274C1.87814 1.59849 1.59769 1.87894 1.42194 2.22388C1.33994 2.38481 1.28362 2.59727 1.2534 2.96706C1.22261 3.34399 1.22213 3.82815 1.22213 4.52269V6.47811C1.22213 7.17265 1.22261 7.65681 1.2534 8.03374C1.28362 8.40353 1.33994 8.61599 1.42194 8.77692C1.59769 9.12186 1.87814 9.4023 2.22308 9.57806C2.384 9.66006 2.59646 9.71638 2.96626 9.7466C3.34319 9.77739 3.82735 9.77787 4.52189 9.77787H6.4773C7.17184 9.77787 7.656 9.77739 8.03293 9.7466C8.40273 9.71638 8.61519 9.66006 8.77612 9.57806C9.12105 9.4023 9.4015 9.12186 9.57725 8.77692C9.65411 8.62608 9.70865 8.42933 9.73998 8.09956C9.77198 7.76276 9.77652 7.33222 9.777 6.72206C9.77726 6.38457 10.0511 6.1112 10.3885 6.11147C10.726 6.11173 10.9994 6.38553 10.9991 6.72301C10.9987 7.32075 10.9951 7.81073 10.9566 8.21516C10.9175 8.62662 10.8394 8.99184 10.6662 9.33176C10.3733 9.90666 9.90585 10.3741 9.33095 10.667C8.96902 10.8514 8.57783 10.9283 8.13245 10.9647C7.69989 11 7.16573 11 6.50348 11H4.49572C3.83346 11 3.2993 11 2.86674 10.9647C2.42137 10.9283 2.03017 10.8514 1.66824 10.667C1.09334 10.3741 0.625936 9.90666 0.333011 9.33176C0.148599 8.96983 0.0717194 8.57863 0.0353308 8.13326C-1.08402e-05 7.7007 -5.95858e-06 7.16653 1.60379e-07 6.50426V4.49653C-5.95858e-06 3.83427 -1.08402e-05 3.3001 0.0353308 2.86754C0.0717194 2.42217 0.148599 2.03097 0.333012 1.66904C0.625936 1.09414 1.09334 0.626736 1.66824 0.33381C2.00816 0.16061 2.37338 0.0824474 2.78484 0.0433547C3.18927 0.00493061 3.67925 0.00132676 4.27699 0.000860659C4.61447 0.000597507 4.88827 0.273968 4.88853 0.611451Z" fill="#0D0D0D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="details-panel">
|
||||
<div class="header" @click="show = !show">
|
||||
<span class="icon"><svg-icon name="dc-details_edit" size="17" /></span>
|
||||
<span class="title">Edit Details</span>
|
||||
<span class="show">
|
||||
<svg-icon name="dc-down_arrow2" size="10" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="content" v-show="show">
|
||||
这是一些设置参数...
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, inject, computed } from 'vue'
|
||||
const props = defineProps({})
|
||||
const show = ref(true)
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.details-panel {
|
||||
position: absolute;
|
||||
top: 2.2rem;
|
||||
right: 3rem;
|
||||
width: 28.8rem;
|
||||
max-height: 80%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.6rem;
|
||||
> div {
|
||||
border: 0.2rem solid #ebebeb;
|
||||
background: #ffffff;
|
||||
border-radius: 0.9rem;
|
||||
box-shadow: 0 1.66rem 2.33rem 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
> .header {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.8rem;
|
||||
padding: 0 1.6rem;
|
||||
> .title {
|
||||
flex: 1;
|
||||
font-family: Semibold;
|
||||
font-size: 1.6rem;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
> .content {
|
||||
padding: 1.6rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
150
src/components/Canvas/DepthCanvas/components/header-tools.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<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>
|
||||
</template>
|
||||
<button class="export" @click="emit('export')">
|
||||
<span class="icon"><svg-icon name="export" size="12" /></span>
|
||||
<span class="text">Export</span>
|
||||
</button>
|
||||
<button class="workbench">
|
||||
<span class="icon"><svg-icon name="dc-workbench" size="20" /></span>
|
||||
<span class="text">Workbench</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, inject, computed } from 'vue'
|
||||
import { TOOLS } from '../manager/ToolManager'
|
||||
const props = defineProps({
|
||||
zoom: { default: 1, type: Number },
|
||||
step: { default: 0.1, type: Number }
|
||||
})
|
||||
const emit = defineEmits(['export', 'import'])
|
||||
const stateManager = inject('stateManager') as any
|
||||
const toolManager = inject('toolManager') as any
|
||||
const tool = computed(() => stateManager.tool.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 tools = ref([
|
||||
{ name: TOOLS.SELECT, icon: 'dc-select', iconSize: 16, disabled: ref(false) },
|
||||
{ name: TOOLS.MOVE, icon: 'dc-move', iconSize: 18, disabled: ref(false) },
|
||||
{ name: TOOLS.BRUSH, icon: 'dc-brush', iconSize: 18, disabled: ref(false) },
|
||||
{ name: TOOLS.ERASER, icon: 'dc-eraser', iconSize: 18, disabled: ref(false) },
|
||||
{ name: TOOLS.IMAGE, icon: 'dc-image', iconSize: 17, disabled: ref(false) },
|
||||
{ name: TOOLS.SELECTBOX, icon: 'dc-selectbox', iconSize: 16, disabled: ref(false) },
|
||||
{ name: TOOLS.RECTANGLE, icon: 'dc-rectangle', iconSize: 16, disabled: ref(false) },
|
||||
{ type: 'line' },
|
||||
{
|
||||
name: TOOLS.UNDO,
|
||||
icon: 'dc-undo',
|
||||
iconSize: 18,
|
||||
disabled: isUndo,
|
||||
on: () => stateManager.undoState()
|
||||
},
|
||||
{
|
||||
name: TOOLS.REDO,
|
||||
icon: 'dc-redo',
|
||||
iconSize: 18,
|
||||
disabled: isRedo,
|
||||
on: () => stateManager.redoState()
|
||||
}
|
||||
])
|
||||
const onClickTool = (tool: any) => {
|
||||
if (tool.disabled?.value) return
|
||||
if (tool.on) {
|
||||
tool.on()
|
||||
} else {
|
||||
toolManager.setTool(tool.name)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.header-tools {
|
||||
position: absolute;
|
||||
top: 2.2rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
height: 5rem;
|
||||
padding: 0.7rem 1.8rem;
|
||||
border: 0.2rem solid #ebebeb;
|
||||
background: #ffffff;
|
||||
border-radius: 0.6rem;
|
||||
box-shadow: 0 1.66rem 2.33rem 0 rgba(0, 0, 0, 0.05);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
gap: 1rem;
|
||||
> .line {
|
||||
width: 0;
|
||||
height: 100%;
|
||||
border-left: 0.2rem solid #d9d9d9;
|
||||
border-radius: 0.2rem;
|
||||
margin: 0 0.6rem;
|
||||
}
|
||||
> .icon {
|
||||
cursor: pointer;
|
||||
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;
|
||||
}
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
> button {
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.8rem;
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
> .export {
|
||||
width: 10rem;
|
||||
height: 3rem;
|
||||
background-color: #0d0d0d;
|
||||
color: #fff;
|
||||
font-size: 1.2rem;
|
||||
border-radius: 0.4rem;
|
||||
}
|
||||
> .workbench {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -2.4rem;
|
||||
transform: translateX(100%);
|
||||
height: 100%;
|
||||
padding: 0 2.4rem;
|
||||
border: 0.2rem solid #ebebeb;
|
||||
background: #ffffff;
|
||||
border-radius: 0.6rem;
|
||||
box-shadow: 0 1.66rem 2.33rem 0 rgba(0, 0, 0, 0.05);
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="layer-panel">
|
||||
<div class="icon" @click="showList = !showList">
|
||||
<svg-icon name="dc-layer" size="20" />
|
||||
</div>
|
||||
<layer-list v-show="showList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, inject, computed } from 'vue'
|
||||
import layerList from './layer-list.vue'
|
||||
const props = defineProps({})
|
||||
const showList = ref(true)
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.layer-panel {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
top: 2.2rem;
|
||||
left: 3rem;
|
||||
max-height: 80%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.6rem;
|
||||
> div {
|
||||
border: 0.2rem solid #ebebeb;
|
||||
background: #ffffff;
|
||||
border-radius: 0.9rem;
|
||||
box-shadow: 0 1.66rem 2.33rem 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
> .icon {
|
||||
flex-shrink: 0;
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
border-radius: 1.2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="layer-item">
|
||||
<div class="drag"><svg-icon name="dc-drag" size="18" /></div>
|
||||
<div class="thumb"></div>
|
||||
<div class="name">
|
||||
<div @dblclick="editName = true" v-if="!editName">{{ layer.name }}</div>
|
||||
<input type="text" v-model="layer.name" v-else @blur="editName = false" />
|
||||
</div>
|
||||
<div class="icons">
|
||||
<span><svg-icon name="dc-show" size="15" /></span>
|
||||
<span><svg-icon name="dc-delete" size="13" /></span>
|
||||
<span><svg-icon name="dc-down_arrow" size="11" /></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, inject, computed } from 'vue'
|
||||
const props = defineProps({
|
||||
layer: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const editName = ref(false)
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.layer-item {
|
||||
width: 100%;
|
||||
height: 9.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 1.4rem;
|
||||
background-color: #fafafa;
|
||||
gap: 1rem;
|
||||
border-bottom: 0.2rem solid #ededed;
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
&:not([draging='true']) {
|
||||
&:hover,
|
||||
&.active {
|
||||
background-color: #ededed;
|
||||
}
|
||||
}
|
||||
&.drag {
|
||||
opacity: 0;
|
||||
}
|
||||
&.ghost,
|
||||
&.chosen {
|
||||
box-shadow: inset 0 0 5px #aaa;
|
||||
background-color: #ededed;
|
||||
}
|
||||
> .drag {
|
||||
padding: 0.3rem;
|
||||
cursor: move;
|
||||
}
|
||||
> .thumb {
|
||||
width: 5.6rem;
|
||||
height: 5.6rem;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
border: 0.2rem solid #ebebeb;
|
||||
background-color: #fff;
|
||||
> img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
> .name {
|
||||
flex: 1;
|
||||
font-size: 1.4rem;
|
||||
|
||||
> div {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
> input {
|
||||
width: 100%;
|
||||
height: 3rem;
|
||||
}
|
||||
}
|
||||
> .icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.8rem;
|
||||
> span {
|
||||
cursor: pointer;
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<div class="layer-list">
|
||||
<div class="header">
|
||||
<div class="left">
|
||||
<span class="icon"><svg-icon name="dc-layer" size="16" /></span>
|
||||
<span class="title">Layer</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<span class="icon"><svg-icon name="add" size="14" /></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<VueDraggable
|
||||
:model-value="sortableRootLayers"
|
||||
@start="handleDragStart"
|
||||
@end="handleDragEnd"
|
||||
class="sortable-layers"
|
||||
:data-container-type="'root'"
|
||||
:data-parent-id="null"
|
||||
:animation="250"
|
||||
:disabled="false"
|
||||
handle=".drag"
|
||||
ghost-class="ghost"
|
||||
chosen-class="chosen"
|
||||
drag-class="drag"
|
||||
:group="{
|
||||
name: 'groupName',
|
||||
pull: true,
|
||||
put: true
|
||||
}"
|
||||
:swap-threshold="0.5"
|
||||
:empty-insert-threshold="5"
|
||||
:force-fallback="false"
|
||||
:fallback-tolerance="3"
|
||||
:scroll-sensitivity="100"
|
||||
:scroll-speed="10"
|
||||
>
|
||||
<layer-item
|
||||
v-for="layer in sortableRootLayers"
|
||||
:key="layer.id"
|
||||
:layer="layer"
|
||||
:draging="draging"
|
||||
/>
|
||||
</VueDraggable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VueDraggable } from 'vue-draggable-plus'
|
||||
import { ref, inject, computed } from 'vue'
|
||||
import layerItem from './layer-item.vue'
|
||||
const draging = ref(false)
|
||||
const sortableRootLayers = ref([
|
||||
{ id: 1, name: 'layer1' },
|
||||
{ id: 2, name: 'layer2' },
|
||||
{ id: 3, name: 'layer3' }
|
||||
])
|
||||
|
||||
const handleDragStart = () => {
|
||||
draging.value = true
|
||||
}
|
||||
const handleDragEnd = (event) => {
|
||||
draging.value = false
|
||||
const { from, to, oldIndex, newIndex, item } = event
|
||||
console.log('🔄 拖拽结束事件:', event)
|
||||
sortableRootLayers.value.splice(newIndex, 0, sortableRootLayers.value.splice(oldIndex, 1)[0])
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.layer-list {
|
||||
flex: 1;
|
||||
width: 36.7rem;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
> .header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 5.4rem;
|
||||
padding: 0 2rem;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 0.1rem solid #c9c9c9;
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
> .left {
|
||||
> .title {
|
||||
font-family: Semibold;
|
||||
font-size: 1.6rem;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
> .right {
|
||||
> .icon {
|
||||
cursor: pointer;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="my-input">
|
||||
<span class="decorate"></span>
|
||||
<span v-show="icon" class="icon">
|
||||
<svg-icon :name="icon" :size="iconSize" size-unit="px" />
|
||||
</span>
|
||||
<span v-show="before" class="before">{{ before }}</span>
|
||||
<input v-bind="attrs" :value="modelValue" @input="onInput" @copy.stop @keydown.stop />
|
||||
<span v-show="after" class="after">{{ after }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, useAttrs, watch } from 'vue'
|
||||
const props = defineProps({
|
||||
modelValue: { type: [String, Number] },
|
||||
icon: { default: '', type: String },
|
||||
iconSize: { default: '10', type: [Number, String] },
|
||||
before: { default: '', type: String },
|
||||
after: { default: '', type: String }
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
const emit = defineEmits(['update:modelValue', 'input'])
|
||||
const onInput = (e) => {
|
||||
var value = e.target.value
|
||||
if (attrs.type === 'number') value = Number(value)
|
||||
emit('update:modelValue', value)
|
||||
emit('input', value)
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.my-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
border: 1px solid rgba(230, 230, 231, 1);
|
||||
border-radius: 1.7px;
|
||||
height: 17px;
|
||||
padding: 0 4px 0 2px;
|
||||
> .decorate {
|
||||
width: 2px;
|
||||
background-color: rgba(230, 230, 231, 1);
|
||||
border-radius: 3px;
|
||||
height: 85%;
|
||||
margin-right: 4px;
|
||||
}
|
||||
> .iconfont {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
margin-right: 2px;
|
||||
}
|
||||
> .before {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
margin-right: 2px;
|
||||
}
|
||||
> .after {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
margin-left: 1px;
|
||||
}
|
||||
> input {
|
||||
font-size: 12px;
|
||||
width: 0;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
outline: none;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
-moz-appearance: textfield; /* Firefox */
|
||||
&::-webkit-outer-spin-button,
|
||||
&::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="my-select">
|
||||
<el-select :model-value="modelValue" @change="onChange" v-bind="attrs">
|
||||
<el-option v-for="v in list" :key="v.value" :label="v.label" :value="v.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, useAttrs, watch } from 'vue'
|
||||
const props = defineProps({
|
||||
modelValue: { required: true },
|
||||
list: { default: () => [], type: [Array, Object] }
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
const onChange = (value) => {
|
||||
emit('update:modelValue', value)
|
||||
emit('change', value)
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.my-select {
|
||||
&:deep(.el-select) {
|
||||
--el-select-input-font-size: 12px;
|
||||
.el-select__wrapper {
|
||||
font-size: 12px;
|
||||
min-height: 0;
|
||||
height: 28px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
.el-select__selected-item,
|
||||
.el-select__input-wrapper,
|
||||
.el-select__placeholder {
|
||||
line-height: normal;
|
||||
}
|
||||
.el-select__input {
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-popper {
|
||||
.el-select-dropdown {
|
||||
li {
|
||||
padding-left: 8px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="my-textarea">
|
||||
<textarea
|
||||
:placeholder="placeholder"
|
||||
:value="modelValue"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
@copy.stop
|
||||
@keydown.stop
|
||||
></textarea>
|
||||
<div class="bths">
|
||||
<button><svg-icon name="mobang" size="10" size-unit="px" /></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, markRaw, onMounted } from 'vue'
|
||||
const emit = defineEmits(['update:modelValue', 'input', 'change'])
|
||||
const props = defineProps({
|
||||
modelValue: { type: String },
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: 'Enter the scene you want to describe...'
|
||||
}
|
||||
})
|
||||
const onInput = (e) => {
|
||||
const value = e.target.value
|
||||
emit('update:modelValue', value)
|
||||
emit('input', value)
|
||||
}
|
||||
const onChange = (e) => {
|
||||
emit('change', e.target.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.my-textarea {
|
||||
width: 100%;
|
||||
height: 115px;
|
||||
border: 1px solid #e4e4e7;
|
||||
border-radius: 10px;
|
||||
padding: 10px 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
> textarea {
|
||||
padding: 0 5px;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
border: none;
|
||||
outline: none;
|
||||
resize: none;
|
||||
font-family: Medium;
|
||||
font-size: 10px;
|
||||
color: #333;
|
||||
&::placeholder {
|
||||
color: #c9c9c9;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
> .bths {
|
||||
padding: 5px 5px 0;
|
||||
> button {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding: 0;
|
||||
border-radius: 4px;
|
||||
background-color: #f0f0f0;
|
||||
border: 1px solid #e4e4e7;
|
||||
&:active {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div class="offset-tool">
|
||||
<div class="input" v-show="showInput">
|
||||
<my-input v-model="left" type="number" before="X" after="%" :min="-100" :max="100" />
|
||||
<my-input v-model="top" type="number" before="Y" after="%" :min="-100" :max="100" />
|
||||
</div>
|
||||
<div
|
||||
class="dish"
|
||||
@mousedown="mousedown"
|
||||
@touchstart="mousedown"
|
||||
ref="dishRef"
|
||||
v-show="showDish"
|
||||
>
|
||||
<img src="/src/assets/images/icon/xyz.png" />
|
||||
<span class="ball" :style="ballStyle"></span>
|
||||
<span class="tip x">X: {{ left }}%</span>
|
||||
<span class="tip y">Y: {{ top }}%</span>
|
||||
<span class="line x"></span>
|
||||
<span class="line y"></span>
|
||||
<span class="line z" :style="lineZStyle"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import MyInput from './my-input.vue'
|
||||
const props = defineProps({
|
||||
modelValue: { type: Object as () => { x: number; y: number } },
|
||||
showInput: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showDish: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue', 'change', 'input'])
|
||||
// 工具的实际坐标 -100 ~ 100
|
||||
const top = ref(Math.round(props.modelValue.y))
|
||||
const left = ref(Math.round(props.modelValue.x))
|
||||
|
||||
// 原点的坐标 0 ~ 100
|
||||
const ballStyle = computed(() => ({
|
||||
top: 50 + top.value / 2 + '%',
|
||||
left: 50 + left.value / 2 + '%'
|
||||
}))
|
||||
watch(
|
||||
() => props.modelValue.x,
|
||||
(v) => (left.value = v)
|
||||
)
|
||||
watch(
|
||||
() => props.modelValue.y,
|
||||
(v) => (top.value = v)
|
||||
)
|
||||
const dishRef = ref<HTMLDivElement>()
|
||||
const mousedown = (e: MouseEvent | TouchEvent) => {
|
||||
if (!dishRef.value) return
|
||||
const mousemove = (e: MouseEvent | TouchEvent) => {
|
||||
if (!dishRef.value) return
|
||||
const rect = dishRef.value.getBoundingClientRect()
|
||||
const X = e.clientX || (e as TouchEvent).touches[0].clientX
|
||||
const Y = e.clientY || (e as TouchEvent).touches[0].clientY
|
||||
var x = ((X - rect.left) / rect.width) * 100
|
||||
var y = ((Y - rect.top) / rect.height) * 100
|
||||
if (x < 0) x = 0
|
||||
if (x > 100) x = 100
|
||||
if (y < 0) y = 0
|
||||
if (y > 100) y = 100
|
||||
left.value = Math.round((x - 50) * 2)
|
||||
top.value = Math.round((y - 50) * 2)
|
||||
onInput()
|
||||
}
|
||||
mousemove(e)
|
||||
const mouseup = () => {
|
||||
onChange()
|
||||
document.removeEventListener('mousemove', mousemove)
|
||||
document.removeEventListener('touchmove', mousemove)
|
||||
document.removeEventListener('mouseup', mouseup)
|
||||
document.removeEventListener('touchend', mouseup)
|
||||
}
|
||||
document.addEventListener('mousemove', mousemove)
|
||||
document.addEventListener('touchmove', mousemove)
|
||||
document.addEventListener('mouseup', mouseup)
|
||||
document.addEventListener('touchend', mouseup)
|
||||
}
|
||||
const onInput = () => {
|
||||
const value = {
|
||||
x: left.value,
|
||||
y: top.value
|
||||
}
|
||||
emit('update:modelValue', value)
|
||||
emit('input', value)
|
||||
}
|
||||
var changeTime: any = null
|
||||
const onChange = () => {
|
||||
clearTimeout(changeTime)
|
||||
changeTime = setTimeout(() => {
|
||||
const value = {
|
||||
x: left.value,
|
||||
y: top.value
|
||||
}
|
||||
emit('update:modelValue', value)
|
||||
emit('change', value)
|
||||
}, 500)
|
||||
}
|
||||
const lineZStyle = computed(() => ({
|
||||
'--rotateZ': calculateAngle(0, 0, left.value, top.value) + 'deg',
|
||||
width: calculateDistance(0, 0, left.value, top.value) / 2 + '%'
|
||||
}))
|
||||
// 计算角度
|
||||
function calculateAngle(x1: number, y1: number, x2: number, y2: number) {
|
||||
const deltaX = x2 - x1
|
||||
const deltaY = y1 - y2
|
||||
let angle = Math.atan2(deltaX, deltaY) * (180 / Math.PI) - 90
|
||||
return angle
|
||||
}
|
||||
// 计算距离
|
||||
function calculateDistance(x1: number, y1: number, x2: number, y2: number) {
|
||||
const deltaX = x2 - x1
|
||||
const deltaY = y2 - y1
|
||||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
|
||||
return distance
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.offset-tool {
|
||||
position: relative;
|
||||
> .input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
> * {
|
||||
flex: 1;
|
||||
margin-right: 10px;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .dish {
|
||||
width: 115px;
|
||||
height: 115px;
|
||||
border: 1px solid #eaeaea;
|
||||
border-radius: 3.4px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
background-color: #f6f6f6;
|
||||
margin-top: 20px;
|
||||
> * {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
> img {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
bottom: 3.5px;
|
||||
right: 3.5px;
|
||||
}
|
||||
> .ball {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 8.5px;
|
||||
height: 8.5px;
|
||||
border: 1px solid #fff;
|
||||
background-color: #333;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0px 0.68px 0.17px 0px rgba(0, 0, 0, 0.26);
|
||||
}
|
||||
> .tip {
|
||||
font-size: 8.5px;
|
||||
color: #000;
|
||||
line-height: 24px;
|
||||
&.x {
|
||||
top: 50%;
|
||||
right: 0%;
|
||||
transform: translate(100%, -50%);
|
||||
padding-left: 6px;
|
||||
}
|
||||
&.y {
|
||||
top: 0%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -100%);
|
||||
}
|
||||
}
|
||||
> .line {
|
||||
border-color: #d9d9d9;
|
||||
border-style: dashed;
|
||||
border-width: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
&.x {
|
||||
width: 100%;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
&.y {
|
||||
height: 100%;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
&.z {
|
||||
width: 50%;
|
||||
border-top-width: 1px;
|
||||
border-color: #454754;
|
||||
transform: translate(0%, -50%) rotateZ(var(--rotateZ));
|
||||
transform-origin: left center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div class="pixel-ratio-selection">
|
||||
<div
|
||||
v-for="v in list"
|
||||
:key="v"
|
||||
:class="{ active: v === modelValue }"
|
||||
@click="onChange(v)"
|
||||
:style="{ '--w': v.split(':')[0], '--h': v.split(':')[1] }"
|
||||
>
|
||||
{{ v }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, defineExpose } from 'vue'
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
const props = defineProps({
|
||||
modelValue: { type: String },
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => ['1:1', '4:3', '3:4', '16:9']
|
||||
}
|
||||
})
|
||||
const data = reactive({})
|
||||
const onChange = (v) => {
|
||||
emit('update:modelValue', v)
|
||||
emit('change', v)
|
||||
}
|
||||
defineExpose({ data })
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.pixel-ratio-selection {
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
border-radius: 6px;
|
||||
background-color: #f0f0f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 17px;
|
||||
user-select: none;
|
||||
> div {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: #7c7c7c;
|
||||
height: 21px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&.active {
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
}
|
||||
&::before {
|
||||
content: '';
|
||||
border-radius: 1px;
|
||||
border: 1px solid #7c7c7c;
|
||||
width: calc(var(--w) / max(var(--w), var(--h)) * 10px);
|
||||
height: calc(var(--h) / max(var(--w), var(--h)) * 10px);
|
||||
margin-right: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
172
src/components/Canvas/DepthCanvas/components/tools/slider.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<div class="slider" :disabled="disabled">
|
||||
<div
|
||||
class="input-range"
|
||||
:style="{
|
||||
'--progress': (value - props.min) / (props.max - props.min)
|
||||
}"
|
||||
>
|
||||
<span class="tip">{{ props.tipFormatter(value) }}</span>
|
||||
<input
|
||||
type="range"
|
||||
v-model="value"
|
||||
v-bind="$attrs"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
:disabled="disabled"
|
||||
:min="props.min"
|
||||
:max="props.max"
|
||||
/>
|
||||
</div>
|
||||
<div class="input" v-show="isInput">
|
||||
<my-input
|
||||
type="number"
|
||||
v-model="value"
|
||||
v-bind="$attrs"
|
||||
@input="onInput"
|
||||
@change="onChange"
|
||||
:disabled="disabled"
|
||||
:min="props.min"
|
||||
:max="props.max"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, watch } from 'vue'
|
||||
import MyInput from './my-input.vue'
|
||||
const props = defineProps({
|
||||
modelValue: { type: Number },
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
min: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
default: 100
|
||||
},
|
||||
tipFormatter: {
|
||||
type: Function,
|
||||
default: (v) => v
|
||||
},
|
||||
isInput: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue', 'change', 'input'])
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(v) => {
|
||||
value.value = v
|
||||
}
|
||||
)
|
||||
const value = ref(props.modelValue)
|
||||
const onInput = () => {
|
||||
if (props.disabled) return
|
||||
const v = Number(value.value)
|
||||
emit('update:modelValue', v)
|
||||
emit('input', v)
|
||||
}
|
||||
const onChange = () => {
|
||||
if (props.disabled) return
|
||||
const v = Number(value.value)
|
||||
emit('update:modelValue', v)
|
||||
emit('change', v)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.slider {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
--input-thumb-size: 8px;
|
||||
--backcolor1: var(--slider-thumb-color1, #4285f4);
|
||||
--backcolor2: var(--slider-thumb-color2, rgba(0, 0, 0, 0.1));
|
||||
&:hover {
|
||||
> .input-range > .tip {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
> .input-range {
|
||||
position: relative;
|
||||
flex: 2;
|
||||
display: flex;
|
||||
> input {
|
||||
width: 100%;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: 3px;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
var(--backcolor1) 0%,
|
||||
var(--backcolor1) calc(var(--progress) * 100%),
|
||||
var(--backcolor2) calc(var(--progress) * 100%),
|
||||
var(--backcolor2) 100%
|
||||
);
|
||||
&::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: var(--input-thumb-size);
|
||||
height: var(--input-thumb-size);
|
||||
border-radius: 50%;
|
||||
background: var(--backcolor1); /* 蓝色滑块 */
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
&::-webkit-slider-thumb:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
> .tip {
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
color: #666;
|
||||
top: calc(var(--input-thumb-size) / -2 - 3.5px);
|
||||
left: calc(
|
||||
(100% - var(--input-thumb-size)) * var(--progress) + var(--input-thumb-size) / 2
|
||||
);
|
||||
transform: translate(-50%, -100%);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
color: white;
|
||||
padding: 3px 5px;
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 97%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 5px solid rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
> .input {
|
||||
flex: 1;
|
||||
margin-left: 10px;
|
||||
> input {
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div class="upload-file">
|
||||
<div class="preview" v-if="url">
|
||||
<img :src="url" @error="onChange(null)" />
|
||||
<div class="close" @click="onChange(null)">
|
||||
<svg-icon name="close-border" size="16" size-unit="px" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control" v-else>
|
||||
<div class="icon"><svg-icon name="upload" size="17" size-unit="px" /></div>
|
||||
<div class="txt">{{ tip }}</div>
|
||||
<div class="btn" @click="onSelectFile">Select File</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, computed } from 'vue'
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
const props = defineProps({
|
||||
modelValue: { type: [File, Object, String, null] },
|
||||
tip: { type: String, default: 'Upload your files' }
|
||||
})
|
||||
const data = reactive({
|
||||
file: null
|
||||
})
|
||||
const url = computed(() => {
|
||||
const type = props.modelValue?.constructor
|
||||
var str = ''
|
||||
if (type === File) {
|
||||
str = URL.createObjectURL(props.modelValue as File)
|
||||
} else if (type === String) {
|
||||
str = props.modelValue as string
|
||||
}
|
||||
return str
|
||||
})
|
||||
const onChange = (v) => {
|
||||
emit('update:modelValue', v)
|
||||
emit('change', v)
|
||||
}
|
||||
const onSelectFile = () => {
|
||||
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]
|
||||
if (file) onChange(file)
|
||||
})
|
||||
input.click()
|
||||
}
|
||||
defineExpose({ data })
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.upload-file {
|
||||
width: 100%;
|
||||
height: 99px;
|
||||
border-radius: 10px;
|
||||
background-color: #f0f0f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
> .control {
|
||||
text-align: center;
|
||||
> .txt {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 8px;
|
||||
color: #7c7c7c;
|
||||
}
|
||||
> .btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0px 0.75px 0px 0px rgba(0, 0, 0, 0.02);
|
||||
min-width: 39px;
|
||||
height: 13px;
|
||||
border-radius: 2.3px;
|
||||
background-color: #fff;
|
||||
font-size: 6px;
|
||||
color: #000;
|
||||
border: 1px solid #d9d9d9;
|
||||
cursor: pointer;
|
||||
&:active {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .preview {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
position: relative;
|
||||
> img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
> .close {
|
||||
position: absolute;
|
||||
top: 0.1px;
|
||||
right: 0.1px;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
141
src/components/Canvas/DepthCanvas/depth-canvas.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div class="depth-canvas">
|
||||
<div class="canvas-container" ref="canvasContainerRef">
|
||||
<canvas ref="canvasRef"></canvas>
|
||||
</div>
|
||||
<layer-panel />
|
||||
<details-panel />
|
||||
<header-tools />
|
||||
<zoom :zoom="1" :step="0.1" is-home />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { fabric } from 'fabric-with-all'
|
||||
import { computed, ref, markRaw, onMounted, nextTick, provide, onBeforeMount } from 'vue'
|
||||
import { NODE_TYPE, NODE_COMPONENT } from './tools/index.d'
|
||||
// 组件
|
||||
import layerPanel from './components/layer-panel/index.vue'
|
||||
import detailsPanel from './components/details-panel/index.vue'
|
||||
import headerTools from './components/header-tools.vue'
|
||||
import zoom from '../components/zoom.vue'
|
||||
|
||||
// 管理器
|
||||
import { StateManager } from './manager/StateManager'
|
||||
import { EventManager } from './manager/EventManager'
|
||||
import { FlowManager } from './manager/FlowManager'
|
||||
import { NodeManager } from './manager/NodeManager'
|
||||
import { ToolManager, TOOLS } from './manager/ToolManager'
|
||||
|
||||
const canvasContainerRef = ref(null)
|
||||
const canvasRef = ref(null)
|
||||
|
||||
const props = defineProps({
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
// 状态管理器
|
||||
const stateManager = new StateManager({})
|
||||
provide('stateManager', stateManager)
|
||||
|
||||
// 事件管理器
|
||||
const eventManager = new EventManager({ stateManager })
|
||||
stateManager.setManager({ eventManager })
|
||||
provide('eventManager', eventManager)
|
||||
|
||||
// 流程管理器
|
||||
const flowManager = new FlowManager({ stateManager })
|
||||
stateManager.setManager({ flowManager })
|
||||
provide('flowManager', flowManager)
|
||||
|
||||
// 节点管理器
|
||||
const nodeManager = new NodeManager({ stateManager })
|
||||
stateManager.setManager({ nodeManager })
|
||||
provide('nodeManager', nodeManager)
|
||||
|
||||
// 工具管理器
|
||||
const toolManager = new ToolManager({ stateManager })
|
||||
stateManager.setManager({ toolManager })
|
||||
provide('toolManager', toolManager)
|
||||
const initCanvas = () => {
|
||||
console.log('OverallCanvas: initCanvas')
|
||||
const canvasViewWidth = canvasContainerRef.value.clientWidth
|
||||
const canvasViewHeight = canvasContainerRef.value.clientHeight
|
||||
const canvasWidth = 750
|
||||
const canvasHeight = 600
|
||||
const canvas = new fabric.Canvas(canvasRef.value, {
|
||||
selection: true,
|
||||
width: canvasViewWidth,
|
||||
height: canvasViewHeight,
|
||||
imageSmoothingEnabled: true, // 启用图像平滑 - 抗锯齿
|
||||
imageSmoothingQuality: 'high', // 设置高质量图像平滑
|
||||
preserveObjectStacking: true,
|
||||
enableRetinaScaling: true,
|
||||
stopContextMenu: true,
|
||||
fireRightClick: true,
|
||||
backgroundColor: '#fff'
|
||||
})
|
||||
canvas.clipPath = new fabric.Rect({
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: canvasWidth,
|
||||
height: canvasHeight
|
||||
})
|
||||
// 画布居中
|
||||
const canvasX = canvasViewWidth / 2 - canvasWidth / 2
|
||||
const canvasY = canvasViewHeight / 2 - canvasHeight / 2
|
||||
canvas.viewportTransform = [1, 0, 0, 1, canvasX, canvasY]
|
||||
// 创建矩形
|
||||
const rect = new fabric.Rect({
|
||||
left: 20,
|
||||
top: 20,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#f00'
|
||||
})
|
||||
canvas.add(rect)
|
||||
//创建圆形
|
||||
const circle = new fabric.Circle({
|
||||
left: 200,
|
||||
top: 200,
|
||||
radius: 50,
|
||||
fill: '#0f0'
|
||||
})
|
||||
canvas.add(circle)
|
||||
}
|
||||
onMounted(() => {
|
||||
initCanvas()
|
||||
})
|
||||
onBeforeMount(() => {
|
||||
// eventManager.removeEvents() // 移除事件
|
||||
})
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import '@vue-flow/core/dist/style.css';
|
||||
@import '@vue-flow/core/dist/theme-default.css';
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.depth-canvas {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
> .canvas-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
> canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
&:deep(.canvas-container) {
|
||||
filter: drop-shadow(0 0 5px rgba(0, 0, 0, 0.3));
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
33
src/components/Canvas/DepthCanvas/index.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<fullscreen-dialog v-model="dialogVisible" hide-destroy>
|
||||
<div class="canvas-box">
|
||||
<depth-canvas :config="config" />
|
||||
</div>
|
||||
</fullscreen-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FullscreenDialog from '../components/fullscreen-dialog.vue'
|
||||
import depthCanvas from './depth-canvas.vue'
|
||||
import { ref } from 'vue'
|
||||
const dialogVisible = ref(false)
|
||||
const config = ref({})
|
||||
const open = (options) => {
|
||||
dialogVisible.value = true
|
||||
config.value = options || {}
|
||||
}
|
||||
const close = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
defineExpose({
|
||||
open,
|
||||
close
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.canvas-box {
|
||||
padding-top: 10rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
84
src/components/Canvas/DepthCanvas/manager/EventManager.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { TOOLS } from "./ToolManager"
|
||||
export class EventManager {
|
||||
stateManager: any
|
||||
vueFlow: any
|
||||
zoom: any
|
||||
constructor(options) {
|
||||
this.stateManager = options.stateManager;
|
||||
this.vueFlow = options.vueFlow
|
||||
this.zoom = this.stateManager.zoom
|
||||
this.registerEvents()
|
||||
}
|
||||
/** 处理视口变化 */
|
||||
handleViewportChange(e: any) {
|
||||
const { zoom } = e
|
||||
this.zoom.value = zoom
|
||||
}
|
||||
/** 处理节点拖动停止 */
|
||||
handleNodeDragStop(e: any) {
|
||||
const { node } = e
|
||||
const { id, position } = node
|
||||
this.stateManager.nodes.value.forEach((item) => {
|
||||
if (item.id === id) {
|
||||
item.position.x = position.x
|
||||
item.position.y = position.y
|
||||
}
|
||||
})
|
||||
this.stateManager.recordState()
|
||||
}
|
||||
/** 处理点击 */
|
||||
handleClick(event: any) {
|
||||
this.stateManager.setActiveNodeID("")
|
||||
const tool = this.stateManager.tool.value
|
||||
if (tool === TOOLS.TEXT) {
|
||||
const { x, y, zoom } = this.vueFlow.value.viewport
|
||||
const position = {
|
||||
x: (event.offsetX - x) / zoom,
|
||||
y: (event.offsetY - y) / zoom
|
||||
}
|
||||
this.stateManager.nodeManager.createTextNode({ position })
|
||||
this.stateManager.toolManager.setTool(TOOLS.SELECT)
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理复制 */
|
||||
handleCopy(event: any, activeNodeID: string) {
|
||||
event.preventDefault()
|
||||
if (!activeNodeID) return console.warn('没有选中节点')
|
||||
this.stateManager.nodeManager.copyNodeById(activeNodeID)
|
||||
}
|
||||
/** 处理删除 */
|
||||
handleDelete(event: any, activeNodeID: string) {
|
||||
event.preventDefault()
|
||||
if (!activeNodeID) return console.warn('没有选中节点')
|
||||
this.stateManager.deleteNode(activeNodeID, { isElMessageBox: true })
|
||||
}
|
||||
/** 处理键盘事件 */
|
||||
handleKeyDown(event: any) {
|
||||
const activeNodeID = this.stateManager.activeNodeID.value;
|
||||
// const shiftKey
|
||||
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, activeNodeID) },
|
||||
{ key: "delete", handler: () => this.handleDelete(event, activeNodeID) },
|
||||
{ key: "ctrl-z", handler: () => this.stateManager.undoState() },
|
||||
{ key: "ctrl-shift-z", handler: () => this.stateManager.redoState() },
|
||||
]
|
||||
list.forEach((v: any) => {
|
||||
if (reg.test(v.key)) v.handler(event)
|
||||
})
|
||||
}
|
||||
/** 注册事件 */
|
||||
registerEvents() {
|
||||
// document.addEventListener('copy', this.handleCopy.bind(this))
|
||||
document.addEventListener('keydown', this.handleKeyDown.bind(this))
|
||||
}
|
||||
/** 删除事件 */
|
||||
removeEvents() {
|
||||
// document.removeEventListener('copy', this.handleCopy.bind(this))
|
||||
document.removeEventListener('keydown', this.handleKeyDown.bind(this))
|
||||
}
|
||||
}
|
||||
26
src/components/Canvas/DepthCanvas/manager/FlowManager.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export class FlowManager {
|
||||
stateManager: any
|
||||
vueFlow: any
|
||||
constructor(options) {
|
||||
this.stateManager = options.stateManager;
|
||||
this.vueFlow = options.vueFlow
|
||||
}
|
||||
setZoom(zoom: number) {
|
||||
this.stateManager.zoom.value = zoom
|
||||
this.vueFlow.value.zoomTo(zoom)
|
||||
}
|
||||
getNodeById(id: string) {
|
||||
return this.vueFlow.value.getNode(id)
|
||||
}
|
||||
getLastNode() {
|
||||
const lastNode = this.stateManager.getLastNode()
|
||||
if (lastNode?.id) {
|
||||
return this.vueFlow.value.getNode(lastNode.id)
|
||||
}
|
||||
return null;
|
||||
}
|
||||
getSubordNodeByID(id: string) {
|
||||
return this.vueFlow.value.getNodes?.find((v) => v.data.superiorID === id)
|
||||
}
|
||||
|
||||
}
|
||||
133
src/components/Canvas/DepthCanvas/manager/NodeManager.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { createId } from '../../tools/tools'
|
||||
import { NODE_DATATYPE, NODE_COMPONENT, NODE_DATATIER } from '../tools/index.d'
|
||||
interface NodeData {
|
||||
type?: string
|
||||
component?: any// 节点组件
|
||||
data?: object// 节点数据
|
||||
tier?: string// 节点层级
|
||||
isHeader?: boolean// 是否显示头
|
||||
superiorID?: string// 上级节点ID
|
||||
disableDelete?: boolean// 是否禁用删除
|
||||
disableCopy?: boolean// 是否禁用复制
|
||||
}
|
||||
interface NodeOptions {
|
||||
id?: string
|
||||
position?: { x: number, y: number }
|
||||
positionX?: number
|
||||
positionY?: number
|
||||
component?: any
|
||||
data?: NodeData
|
||||
}// 不可传入type class (内部使用)
|
||||
|
||||
export class NodeManager {
|
||||
stateManager: any
|
||||
vueFlow: any
|
||||
nodesep = 100 // 节点间距
|
||||
ranksep = 100 // 层级间距
|
||||
constructor(options) {
|
||||
this.stateManager = options.stateManager;
|
||||
this.vueFlow = options.vueFlow
|
||||
}
|
||||
|
||||
/** 删除节点 */
|
||||
deleteNode(id: string) {
|
||||
this.stateManager.deleteNode(id)
|
||||
}
|
||||
/** 添加节点 */
|
||||
addNode(node: any) {
|
||||
this.stateManager.addNode(node)
|
||||
}
|
||||
|
||||
/** 创建节点 */
|
||||
createNode(options: NodeOptions) {
|
||||
const superiorID = options?.data?.superiorID
|
||||
const snode = superiorID ? this.stateManager.flowManager.getNodeById(superiorID) : this.stateManager.flowManager.getLastNode();
|
||||
const id = options.id || createId()
|
||||
const positionX = options.positionX || 0
|
||||
const positionY = options.positionY || 0
|
||||
const position = options.position ||
|
||||
(!snode ?
|
||||
{ x: positionX, y: positionY } :
|
||||
{
|
||||
x: snode.position.x + snode.dimensions.width + this.nodesep + positionX,
|
||||
y: snode.position.y + positionY
|
||||
})
|
||||
const data = options?.data || {}
|
||||
data['component'] = options.component
|
||||
const options_ = {
|
||||
id,
|
||||
position,
|
||||
data
|
||||
}
|
||||
this.addNode(options_)
|
||||
return options_;
|
||||
}
|
||||
/** 创建结果节点 */
|
||||
createResultNode(options?: NodeOptions) {
|
||||
const options_ = {
|
||||
...(options ? options : {}),
|
||||
component: NODE_COMPONENT.RESULT_IMAGE,
|
||||
data: {
|
||||
tier: NODE_DATATIER.RESULT_IMAGE,
|
||||
type: NODE_DATATYPE.RESULT_IMAGE,
|
||||
isHeader: true,
|
||||
...(options?.data || {}),
|
||||
},
|
||||
}
|
||||
return this.createNode(options_)
|
||||
}
|
||||
/** 创建卡片选择节点 */
|
||||
createCardsSelect(options?: NodeOptions) {
|
||||
const options_ = {
|
||||
...(options ? options : {}),
|
||||
component: NODE_COMPONENT.CARD,
|
||||
positionY: 50,
|
||||
data: {
|
||||
tier: NODE_DATATIER.CARDS_SELECT,
|
||||
type: NODE_DATATYPE.CARDS_SELECT,
|
||||
...(options?.data || {}),
|
||||
},
|
||||
}
|
||||
return this.createNode(options_)
|
||||
}
|
||||
/** 创建卡片节点 */
|
||||
createCardNode(options?: NodeOptions) {
|
||||
const options_ = {
|
||||
...(options ? options : {}),
|
||||
component: NODE_COMPONENT.CARD,
|
||||
data: {
|
||||
...(options?.data || {}),
|
||||
}
|
||||
}
|
||||
return this.createNode(options_)
|
||||
}
|
||||
/** 创建文本节点 */
|
||||
createTextNode(options?: NodeOptions) {
|
||||
const options_ = {
|
||||
...(options ? options : {}),
|
||||
component: NODE_COMPONENT.TEXT,
|
||||
data: {
|
||||
...(options?.data || {}),
|
||||
}
|
||||
}
|
||||
return this.createNode(options_)
|
||||
}
|
||||
|
||||
copyNodeById(id: string) {
|
||||
const node = this.stateManager.getNodeById(id)
|
||||
const flowNode = this.stateManager.flowManager.getNodeById(id)
|
||||
if (!node) return console.warn(`${id}找不到对应节点`)
|
||||
if (node.data?.disableCopy) return console.warn(`${id}节点已禁用复制`)
|
||||
const node_ = {
|
||||
...JSON.parse(JSON.stringify(node)),
|
||||
id: createId(),
|
||||
position: {
|
||||
x: node.position.x,
|
||||
y: node.position.y + (flowNode?.dimensions?.height || 0) + this.ranksep,
|
||||
}
|
||||
}
|
||||
delete node_.data?.superiorID
|
||||
delete node_.data?.disableDelete
|
||||
this.stateManager.addNode(node_)
|
||||
}
|
||||
}
|
||||
186
src/components/Canvas/DepthCanvas/manager/StateManager.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import { ref, computed } from "vue";
|
||||
import { NODE_TYPE } from '../tools/index.d'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import i18n from '@/lang'
|
||||
const t = i18n.global.t
|
||||
|
||||
export interface NodesItem {
|
||||
id: string
|
||||
type: string
|
||||
class: string
|
||||
position: { x: number, y: number }
|
||||
data: { component: any, type: string, superiorID?: string }
|
||||
}
|
||||
export class StateManager {
|
||||
vueFlow: any
|
||||
activeNodeID: any
|
||||
nodes: any
|
||||
nodes_: any
|
||||
edges: any
|
||||
zoom: any
|
||||
tool: any
|
||||
cursor: any
|
||||
// 节点是否可拖动
|
||||
nodesDraggable: any
|
||||
// 拖动时是否可以平移画布
|
||||
panOnDrag: any
|
||||
|
||||
// 历史记录-撤回/重做
|
||||
mxHistory: any
|
||||
historyList: any
|
||||
historyIndex: any
|
||||
|
||||
// 管理器
|
||||
eventManager: any
|
||||
flowManager: any
|
||||
nodeManager: any
|
||||
toolManager: any
|
||||
// 设置管理器
|
||||
setManager(options) {
|
||||
options.eventManager && (this.eventManager = options.eventManager)
|
||||
options.flowManager && (this.flowManager = options.flowManager)
|
||||
options.nodeManager && (this.nodeManager = options.nodeManager)
|
||||
options.toolManager && (this.toolManager = options.toolManager)
|
||||
}
|
||||
constructor(options) {
|
||||
this.vueFlow = options.vueFlow
|
||||
this.zoom = ref(1)
|
||||
this.tool = ref("")
|
||||
this.cursor = ref("")
|
||||
this.nodesDraggable = ref(false)
|
||||
this.panOnDrag = ref(false)
|
||||
this.mxHistory = ref(50)
|
||||
this.historyList = ref([])
|
||||
this.historyIndex = ref(0)
|
||||
|
||||
this.activeNodeID = ref("")
|
||||
this.nodes = ref<NodesItem[]>([]);
|
||||
this.nodes_ = computed(() => {
|
||||
return this.nodes.value.map((node, index) => {
|
||||
const obj = node;
|
||||
const superiorID = node.data.superiorID;
|
||||
const isSuperior = this.nodes.value.some((v) => v.id === superiorID)
|
||||
const isSubord = this.nodes.value.some((v) => v.data.superiorID === node.id)
|
||||
if (!isSuperior && isSubord) {// 没有上级 有下级
|
||||
obj.type = NODE_TYPE.INPUT;
|
||||
} else if (isSuperior && isSubord) {// 有上级 有下级
|
||||
obj.type = NODE_TYPE.SECONDARY;
|
||||
} else if (isSuperior && !isSubord) {// 有上级 没有下级
|
||||
obj.type = NODE_TYPE.OUTPUT;
|
||||
} else {// 其他情况-没有上级 没有下级
|
||||
obj.type = NODE_TYPE.ALONE;
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
|
||||
this.edges = computed(() => {
|
||||
const arr = []
|
||||
this.nodes.value.forEach((node, index) => {
|
||||
const superiorID = node.data.superiorID;
|
||||
const isSuperior = this.nodes.value.some((v) => v.id === superiorID)
|
||||
if (superiorID && isSuperior) {
|
||||
const source = node.data.superiorID
|
||||
const target = node.id
|
||||
arr.push({
|
||||
id: `el-${source}-${target}`,
|
||||
source: source,
|
||||
target: target,
|
||||
selectable: false,
|
||||
type: 'default'
|
||||
})
|
||||
}
|
||||
})
|
||||
return arr
|
||||
})
|
||||
|
||||
}
|
||||
/** 设置激活节点 */
|
||||
setActiveNodeID(id: string) { this.activeNodeID.value = id }
|
||||
/** 添加节点 */
|
||||
addNode(node: NodesItem) {
|
||||
this.nodes.value.push(node);
|
||||
this.recordState()
|
||||
}
|
||||
/** 删除节点 */
|
||||
async deleteNode(id: string, { isElMessageBox } = { isElMessageBox: false }) {
|
||||
const node = this.getNodeById(id)
|
||||
if (!node) return console.warn(`没有找到指定id:${id}`)
|
||||
if (node.data.disableDelete) return console.warn('该节点禁用删除')
|
||||
let deletePromise: any = true
|
||||
if (isElMessageBox) {
|
||||
deletePromise = await new Promise<void>((resolve, reject) => {
|
||||
ElMessageBox.confirm(
|
||||
t('flowCanvas.deleteCardConfirm'),
|
||||
'',
|
||||
{
|
||||
confirmButtonText: t('flowCanvas.confirm'),
|
||||
cancelButtonText: t('flowCanvas.cancel'),
|
||||
}
|
||||
).then(() => {
|
||||
resolve(true)
|
||||
}).catch(() => {
|
||||
resolve(false)
|
||||
})
|
||||
})
|
||||
}
|
||||
if (!deletePromise) return console.log('删除操作被取消')
|
||||
this.nodes.value = this.nodes.value.filter((node: NodesItem) => node.id !== id)
|
||||
this.recordState()
|
||||
}
|
||||
/** 获取节点 */
|
||||
getNodeById(id: string) { return this.nodes.value.find((node: NodesItem) => node.id === id) }
|
||||
/** 获取下级节点 */
|
||||
getSubordNodeByID(id: string) { return this.nodes.value.find((node: NodesItem) => node.data.superiorID === id) }
|
||||
getLastNode() { return this.nodes.value[this.nodes.value.length - 1] }
|
||||
/** 设置工具 */
|
||||
setTool(tool: string) { this.tool.value = tool }
|
||||
/** 设置光标 */
|
||||
setCursor(v: string) { this.cursor.value = v }
|
||||
/** 设置节点是否可拖动 */
|
||||
setNodesDraggable(v: boolean) { this.nodesDraggable.value = v }
|
||||
/** 设置是否可以平移画布 */
|
||||
setPanOnDrag(v: boolean) { this.panOnDrag.value = v }
|
||||
/** 设置节点层级至最顶部 */
|
||||
bringToFont(id) {
|
||||
const fromIndex = this.nodes.value.findIndex(item => item.id === id)
|
||||
if (fromIndex === -1) return console.warn(`没有找到指定id:${id}`)
|
||||
this.nodes.value.splice(this.nodes.value.length - 1, 0, ...this.nodes.value.splice(fromIndex, 1))
|
||||
}
|
||||
/** 设置节点层级至最低部 */
|
||||
sendToBack(id) {
|
||||
const fromIndex = this.nodes.value.findIndex(item => item.id === id)
|
||||
if (fromIndex === -1) return console.warn(`没有找到指定id:${id}`)
|
||||
this.nodes.value.splice(0, 0, ...this.nodes.value.splice(fromIndex, 1))
|
||||
}
|
||||
/** 记录状态 */
|
||||
recordState() {
|
||||
if (this.historyIndex.value < this.historyList.value.length - 1) {
|
||||
this.historyList.value.splice(this.historyIndex.value + 1)
|
||||
}
|
||||
const state = {
|
||||
nodes: JSON.stringify(this.nodes.value)
|
||||
}
|
||||
this.historyList.value.push(state)
|
||||
const size = this.historyList.value.length - this.mxHistory.value
|
||||
if (size > 0) this.historyList.value.splice(0, size)
|
||||
this.historyIndex.value = this.historyList.value.length - 1
|
||||
}
|
||||
/** 撤回状态 */
|
||||
undoState() {
|
||||
var index = this.historyIndex.value - 1
|
||||
const state = this.historyList.value[index]
|
||||
if (!state) return
|
||||
this.historyIndex.value = index
|
||||
this.nodes.value = JSON.parse(state.nodes)
|
||||
}
|
||||
/** 重做状态 */
|
||||
redoState() {
|
||||
var index = this.historyIndex.value + 1
|
||||
const state = this.historyList.value[index]
|
||||
if (!state) return
|
||||
this.historyIndex.value = index
|
||||
this.nodes.value = JSON.parse(state.nodes)
|
||||
}
|
||||
|
||||
}
|
||||
52
src/components/Canvas/DepthCanvas/manager/ToolManager.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
export const TOOLS = {
|
||||
SELECT: "SELECT",
|
||||
MOVE: "MOVE",
|
||||
BRUSH: "BRUSH",
|
||||
ERASER: "ERASER",
|
||||
IMAGE: "IMAGE",
|
||||
SELECTBOX: "SELECTBOX",
|
||||
RECTANGLE: "RECTANGLE",
|
||||
TEXT: "TEXT",
|
||||
UNDO: "UNDO",
|
||||
REDO: "REDO",
|
||||
}
|
||||
export const tools = [
|
||||
/** 选择工具 */
|
||||
{
|
||||
name: TOOLS.SELECT,
|
||||
nodesDraggable: true,
|
||||
panOnDrag: false,
|
||||
},
|
||||
/** 移动工具 */
|
||||
{
|
||||
name: TOOLS.MOVE,
|
||||
nodesDraggable: false,
|
||||
panOnDrag: true,
|
||||
},
|
||||
/** 文本工具 */
|
||||
{
|
||||
name: TOOLS.TEXT,
|
||||
cursor: "text",
|
||||
nodesDraggable: false,
|
||||
panOnDrag: false,
|
||||
},
|
||||
|
||||
]
|
||||
export class ToolManager {
|
||||
stateManager: any
|
||||
vueFlow: any
|
||||
constructor(options) {
|
||||
this.stateManager = options.stateManager;
|
||||
this.vueFlow = options.vueFlow
|
||||
this.setTool(TOOLS.SELECT)
|
||||
}
|
||||
setTool(value: string) {
|
||||
const tool = tools.find((t) => t.name === value)
|
||||
if (!tool) return console.warn(`工具${tool}不存在`)
|
||||
this.stateManager.tool.value = tool.name
|
||||
this.stateManager.setNodesDraggable(!!tool.nodesDraggable)
|
||||
this.stateManager.setPanOnDrag(!!tool.panOnDrag)
|
||||
this.stateManager.setCursor(tool.cursor || "")
|
||||
}
|
||||
|
||||
}
|
||||
45
src/components/Canvas/DepthCanvas/tools/index.d.ts
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 组件
|
||||
*/
|
||||
export const NODE_COMPONENT = {
|
||||
RESULT_IMAGE: 'result-image',
|
||||
CARD: 'card',
|
||||
TEXT: 'text',
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点类型
|
||||
*/
|
||||
export const NODE_TYPE = {
|
||||
INPUT: 'InputNode',
|
||||
SECONDARY: 'SecondaryNode',
|
||||
OUTPUT: 'OutputNode',
|
||||
ALONE: 'AloneNode',
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点数据类型
|
||||
*/
|
||||
export const NODE_DATATYPE = {
|
||||
RESULT_IMAGE: 'result-image',
|
||||
CARDS_SELECT: 'cards-select',
|
||||
TO_REAL_STYLE: 'to-real-style',
|
||||
SURFACE_EDIT: 'surface-edit',
|
||||
SCENE_COMPOSITION: 'scene-composition',
|
||||
COLOR_PALETTE: 'color-palette',
|
||||
TO_3D_MODEL: 'to-3d-model',
|
||||
TO_3VIEW: 'to-3view',
|
||||
}
|
||||
/**
|
||||
* 节点数据层级
|
||||
*/
|
||||
export const NODE_DATATIER = {
|
||||
RESULT_IMAGE: 0,
|
||||
CARDS_SELECT: 0,
|
||||
TO_REAL_STYLE: 1,
|
||||
SURFACE_EDIT: 1,
|
||||
SCENE_COMPOSITION: 2,
|
||||
COLOR_PALETTE: 2,
|
||||
TO_3D_MODEL: 2,
|
||||
TO_3VIEW: 3,
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
<span class="icon" @click="onDownload">
|
||||
<svg-icon name="download" size="20" size-unit="px" />
|
||||
</span>
|
||||
<button class="edit">
|
||||
<button class="edit" @click="onEdit">
|
||||
<span class="icon"><svg-icon name="edit" size="13" /></span>
|
||||
<span class="text">Edit</span>
|
||||
</button>
|
||||
@@ -41,6 +41,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import myEvent from '@/utils/myEvent'
|
||||
import { downloadImage } from '../../../tools/tools'
|
||||
import { reactive, ref, onBeforeUnmount, useAttrs, inject, watch } from 'vue'
|
||||
const openImagePreview = inject('openImagePreview') as (url: string) => void
|
||||
@@ -129,6 +130,9 @@
|
||||
const hideMenu = () => {
|
||||
showMenu.value = false
|
||||
}
|
||||
const onEdit = () => {
|
||||
myEvent.emit('openDepthCanvas', { url: data.url })
|
||||
}
|
||||
document.addEventListener('mousedown', hideMenu)
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('mousedown', hideMenu)
|
||||
@@ -183,7 +187,7 @@
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
background-color: #ED8936;
|
||||
background-color: #ed8936;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
|
||||
@@ -68,6 +68,7 @@ export default {
|
||||
general: 'General',
|
||||
profile: 'Profile',
|
||||
learnMore: 'Learn More',
|
||||
userProfile: 'User Profile Photo',
|
||||
userName: 'User Name',
|
||||
email: 'Email',
|
||||
language: 'Language',
|
||||
|
||||
@@ -69,6 +69,7 @@ export default {
|
||||
general: '通用',
|
||||
profile: '个人资料',
|
||||
learnMore: '了解更多',
|
||||
userProfile: '用户个人资料照片',
|
||||
userName: '用户名',
|
||||
email: '邮箱',
|
||||
language: '语言',
|
||||
|
||||
@@ -132,7 +132,7 @@ const {} = toRefs(data)
|
||||
:with-header="false"
|
||||
>
|
||||
<div class="versionTreeTitle">
|
||||
<span>Version Tree: Retro Sofa Sketch</span>
|
||||
<span>Version Tree</span>
|
||||
<div class="closeBtn" @click="versionTreeData.drawer = false">
|
||||
<div class="closeIcon">
|
||||
<SvgIcon name="close" />
|
||||
@@ -219,7 +219,7 @@ const {} = toRefs(data)
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 8px;
|
||||
border-radius: .8rem;
|
||||
border: 1.5px solid #D9D9D9;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
> .versionExport{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
</div>
|
||||
<setting />
|
||||
<flow-canvas ref="flowCanvasRef" />
|
||||
<depth-canvas ref="depthCanvasRef" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -26,16 +27,21 @@
|
||||
import setting from './setting/index.vue'
|
||||
import { useUserInfoStore, useGlobalStore } from '@/stores'
|
||||
import FlowCanvas from '@/components/Canvas/FlowCanvas/index.vue'
|
||||
import DepthCanvas from '@/components/Canvas/DepthCanvas/index.vue'
|
||||
import myEvent from '@/utils/myEvent'
|
||||
const userInfoStore = useUserInfoStore()
|
||||
const globalStore = useGlobalStore()
|
||||
userInfoStore.getUserInfo()
|
||||
const isAnimation = computed(() => globalStore.state.homeAnimation)
|
||||
|
||||
const flowCanvasRef = ref(null)
|
||||
const openFlowCanvas = (config) => {
|
||||
flowCanvasRef.value.open(config)
|
||||
}
|
||||
const openFlowCanvas = (config) => flowCanvasRef.value.open(config)
|
||||
myEvent.add('openFlowCanvas', openFlowCanvas)
|
||||
|
||||
const depthCanvasRef = ref(null)
|
||||
const openDepthCanvas = (config) => depthCanvasRef.value.open(config)
|
||||
myEvent.add('openDepthCanvas', openDepthCanvas)
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
globalStore.setHomeAnimation(false)
|
||||
@@ -43,6 +49,7 @@
|
||||
})
|
||||
onUnmounted(() => {
|
||||
myEvent.remove('openFlowCanvas', openFlowCanvas)
|
||||
myEvent.remove('openDepthCanvas', openDepthCanvas)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
<span class="icon"><svg-icon name="home" size="24" /></span>
|
||||
<span class="title" v-show="!isCollapse">{{ $t('Home.home') }}</span>
|
||||
</div> -->
|
||||
<div class="menu-item" @click="onTest">
|
||||
<span class="title">TEST</span>
|
||||
</div>
|
||||
<div class="menu-item" @click="onHistory" :class="{ active: showHistory }">
|
||||
<span class="icon"><svg-icon name="history" size="24" /></span>
|
||||
<span class="title" v-show="!isCollapse">{{ $t('Home.history') }}</span>
|
||||
@@ -81,6 +78,13 @@
|
||||
</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>
|
||||
|
||||
@@ -108,8 +112,10 @@
|
||||
router.push({ name: 'mainInput' })
|
||||
}
|
||||
const onHome = () => {}
|
||||
const onTest = () => {
|
||||
router.push({ name: 'test' })
|
||||
const onCanvas = (depth: boolean) => {
|
||||
const query = {}
|
||||
if (depth) query['depth'] = '1'
|
||||
router.push({ name: 'test', query })
|
||||
}
|
||||
const onHistory = () => {
|
||||
if (isCollapse.value) {
|
||||
@@ -353,14 +359,14 @@
|
||||
}
|
||||
.history-item-menu {
|
||||
user-select: none;
|
||||
|
||||
|
||||
> div {
|
||||
cursor: pointer;
|
||||
padding: 0.6rem .7rem;
|
||||
padding: 0.6rem 0.7rem;
|
||||
display: flex;
|
||||
font-size: 1.3rem;
|
||||
> .icon{
|
||||
margin-right: .8rem;
|
||||
> .icon {
|
||||
margin-right: 0.8rem;
|
||||
}
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
<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>
|
||||
</div>
|
||||
<div>
|
||||
<div class="label">{{ $t('Home.userName') }}</div>
|
||||
<div class="value">{{ userInfo?.username || '------' }}</div>
|
||||
@@ -41,6 +50,10 @@
|
||||
const logout = () => {
|
||||
userInfoStore.logOut()
|
||||
}
|
||||
const onProfileEdit = () => {
|
||||
// MyEvent.emit('openProfileEditDialog')
|
||||
console.log('openProfileEditDialog')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@@ -54,4 +67,28 @@
|
||||
color: #fff;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.profile {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
position: relative;
|
||||
> img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
> .edit {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 2.3rem;
|
||||
height: 2.3rem;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 1px 4px 0px #1a0f011f;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -111,7 +111,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 6rem;
|
||||
min-height: 6rem;
|
||||
padding: 1rem 0;
|
||||
border-bottom: 0.1rem solid rgba(0, 0, 0, 0.1);
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div class="test">
|
||||
<button @click="openCanvas">打开画布</button>
|
||||
<button @click="openDepthCanvas">打开深度画布</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -9,13 +10,20 @@
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const url =
|
||||
'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__'
|
||||
const openCanvas = () => {
|
||||
myEvent.emit('openFlowCanvas', {
|
||||
url: '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__'
|
||||
})
|
||||
myEvent.emit('openFlowCanvas', { url })
|
||||
}
|
||||
const openDepthCanvas = () => {
|
||||
myEvent.emit('openDepthCanvas', { url })
|
||||
}
|
||||
onMounted(() => {
|
||||
openCanvas()
|
||||
if (route.query.depth) {
|
||||
openDepthCanvas()
|
||||
} else {
|
||||
openCanvas()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -28,7 +36,8 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
> p {
|
||||
gap: 2rem;
|
||||
> button {
|
||||
font-size: 10rem;
|
||||
}
|
||||
}
|
||||
|
||||