Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/FiDA_Front

This commit is contained in:
2026-03-20 17:07:05 +08:00
25 changed files with 675 additions and 84 deletions

41
src/api/depth-canvas.ts Normal file
View File

@@ -0,0 +1,41 @@
import request from '@/utils/request'
/**
* 获取深度画布
* @param id depth id
* @returns 深度画布数据
*/
export const getDepthCanvas = (id: string) => {
return request({
url: `/api/deep-canvas/${id}`,
method: 'get',
loading: true,
})
}
/**
* 保存深度画布
* @param data 保存depth的画布数据
* @returns 保存结果
*/
export const saveDepthCanvas = (data: object) => {
return request({
url: `/api/deep-canvas/update`,
method: 'put',
data,
loading: true,
})
}
/**
* 删除深度画布
* @param id depth id
* @returns 删除结果
*/
export const deleteDepthCanvas = (id: string) => {
return request({
url: `/api/deep-canvas/${id}`,
method: 'delete',
loading: true,
})
}

View File

@@ -0,0 +1,4 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.00048 3.33301C9.00048 2.98775 8.73787 2.70359 8.40152 2.6696L8.33381 2.66634H7.66715C7.29896 2.66634 7.00048 2.96482 7.00048 3.33301V5.11296C7.00048 5.35112 6.8734 5.57134 6.66715 5.69043C6.46092 5.8095 6.20673 5.80944 6.00048 5.69043L4.45881 4.80046C4.14019 4.61654 3.73288 4.72564 3.54866 5.0446H3.54801L3.21532 5.62142C3.03109 5.94051 3.14031 6.34826 3.45881 6.53223L4.99983 7.42155C5.20601 7.5406 5.33309 7.76093 5.33316 7.99902C5.33316 8.23709 5.20596 8.45737 4.99983 8.5765L3.45881 9.46712C3.14031 9.65109 3.03109 10.0588 3.21532 10.3779L3.54801 10.9548H3.54866L3.58577 11.012C3.78349 11.2862 4.16011 11.3713 4.45881 11.1989L6.00048 10.3083C6.20669 10.1893 6.46093 10.1892 6.66715 10.3083C6.87339 10.4274 7.00048 10.6476 7.00048 10.8857V12.6663L7.00374 12.7347C7.03796 13.0708 7.32203 13.333 7.66715 13.333H8.33381C8.70196 13.3329 9.00048 13.0345 9.00048 12.6663V10.8857C9.00048 10.6475 9.1275 10.4273 9.33381 10.3083C9.54007 10.1892 9.79426 10.1892 10.0005 10.3083L11.5421 11.1989L11.6027 11.2301C11.9107 11.3685 12.2797 11.2538 12.4523 10.9548L12.7856 10.3779C12.9698 10.0589 12.8605 9.65117 12.5421 9.46712L11.0005 8.5765C10.7943 8.45738 10.6671 8.23713 10.6671 7.99902C10.6672 7.76086 10.7942 7.54058 11.0005 7.42155L12.5421 6.53223L12.5994 6.49512C12.873 6.29748 12.9583 5.92044 12.7856 5.62142L12.4523 5.0446C12.2682 4.72566 11.8608 4.61662 11.5421 4.80046L10.0005 5.69043C9.79426 5.80947 9.54004 5.80943 9.33381 5.69043C9.12755 5.57134 9.00048 5.35113 9.00048 5.11296V3.33301ZM10.3338 3.95801L10.8755 3.64551C11.8322 3.0934 13.0551 3.42161 13.6073 4.37793H13.6066L13.9399 4.95475H13.9406C14.4926 5.91099 14.1654 7.13469 13.2088 7.68717H13.2082L12.6671 7.99902L13.2088 8.31217L13.2967 8.36621C14.1849 8.94119 14.4753 10.1183 13.9406 11.0446H13.9399L13.6073 11.6214C13.0551 12.5777 11.8322 12.906 10.8755 12.3538L10.3338 12.0407V12.6663C10.3338 13.7364 9.49335 14.6101 8.43668 14.6637L8.33381 14.6663H7.66715C6.56258 14.6663 5.66715 13.7709 5.66715 12.6663V12.0407L5.12548 12.3538C4.19859 12.8888 3.02167 12.5976 2.44709 11.7093L2.39371 11.6214L2.06038 11.0446C1.50835 10.0884 1.83501 8.86475 2.7915 8.31217L3.33251 7.99902L2.79215 7.68717C1.83545 7.13468 1.50829 5.911 2.06038 4.95475L2.39371 4.37793L2.44709 4.29004C3.02167 3.40179 4.1986 3.1105 5.12548 3.64551L5.66715 3.95801V3.33301C5.66715 2.22844 6.56258 1.33301 7.66715 1.33301H8.33381L8.43668 1.33561C9.49335 1.38923 10.3338 2.26296 10.3338 3.33301V3.95801Z" fill="#0D0D0D"/>
<path d="M14.6187 0.93143C14.7484 0.578846 15.2469 0.578846 15.3806 0.93143L15.5914 1.50286C15.6319 1.61228 15.7211 1.70144 15.8305 1.74197L16.4019 1.95271C16.7545 2.08239 16.7545 2.58087 16.4019 2.71461L15.8305 2.92535C15.7211 2.96588 15.6319 3.05504 15.5914 3.16446L15.3806 3.73589C15.2509 4.08847 14.7525 4.08847 14.6187 3.73589L14.408 3.16446C14.3675 3.05504 14.2783 2.96588 14.1689 2.92535L13.5974 2.71461C13.2449 2.58493 13.2449 2.08644 13.5974 1.95271L14.1689 1.74197C14.2783 1.70144 14.3675 1.61228 14.408 1.50286L14.6187 0.93143Z" fill="#FF7A51"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,4 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.2494 4.16797C11.2494 3.73639 10.9211 3.3812 10.5007 3.3387L10.416 3.33464H9.58271C9.12248 3.33464 8.74938 3.70773 8.74938 4.16797V6.3929C8.74938 6.69061 8.59052 6.96588 8.33271 7.11475C8.07493 7.26358 7.75719 7.26351 7.49938 7.11475L5.5723 6.00228C5.17401 5.77239 4.66488 5.90876 4.4346 6.30745H4.43379L4.01793 7.02848C3.78765 7.42735 3.92416 7.93703 4.3223 8.16699L6.24857 9.27865C6.50629 9.42746 6.66514 9.70287 6.66523 10.0005C6.66523 10.2981 6.50622 10.5734 6.24857 10.7223L4.3223 11.8356C3.92416 12.0656 3.78764 12.5752 4.01793 12.9741L4.43379 13.6951H4.4346L4.48099 13.7668C4.72814 14.1094 5.19891 14.2158 5.5723 14.0003L7.49938 12.887C7.75714 12.7383 8.07495 12.7383 8.33271 12.887C8.59052 13.0359 8.74938 13.3112 8.74938 13.6089V15.8346L8.75345 15.9201C8.79623 16.3402 9.15132 16.668 9.58271 16.668H10.416C10.8762 16.6679 11.2494 16.2948 11.2494 15.8346V13.6089C11.2494 13.3111 11.4082 13.0359 11.666 12.887C11.9239 12.7383 12.2416 12.7382 12.4994 12.887L14.4265 14.0003L14.5021 14.0394C14.8872 14.2123 15.3484 14.0689 15.5642 13.6951L15.9808 12.9741C16.211 12.5754 16.0744 12.0657 15.6765 11.8356L13.7494 10.7223C13.4916 10.5734 13.3327 10.2981 13.3327 10.0005C13.3328 9.70279 13.4915 9.42743 13.7494 9.27865L15.6765 8.16699L15.7481 8.12061C16.0901 7.87355 16.1966 7.40226 15.9808 7.02848L15.5642 6.30745C15.334 5.90878 14.8248 5.77249 14.4265 6.00228L12.4994 7.11475C12.2416 7.26355 11.9238 7.2635 11.666 7.11475C11.4082 6.96588 11.2494 6.69062 11.2494 6.3929V4.16797ZM12.916 4.94922L13.5931 4.55859C14.789 3.86845 16.3177 4.27872 17.0078 5.47412H17.007L17.4237 6.19515H17.4245C18.1145 7.39045 17.7055 8.92007 16.5098 9.61068H16.509L15.8327 10.0005L16.5098 10.3919L16.6197 10.4595C17.7299 11.1782 18.0929 12.6496 17.4245 13.8075H17.4237L17.0078 14.5285C16.3177 15.7239 14.789 16.1342 13.5931 15.444L12.916 15.0526V15.8346C12.916 17.1722 11.8655 18.2644 10.5446 18.3314L10.416 18.3346H9.58271C8.202 18.3346 7.08271 17.2153 7.08271 15.8346V15.0526L6.40563 15.444C5.24702 16.1128 3.77587 15.7487 3.05765 14.6383L2.99092 14.5285L2.57425 13.8075C1.88421 12.6123 2.29255 11.0826 3.48815 10.3919L4.16442 10.0005L3.48896 9.61068C2.2931 8.92005 1.88414 7.39045 2.57425 6.19515L2.99092 5.47412L3.05765 5.36426C3.77587 4.25395 5.24703 3.88984 6.40563 4.55859L7.08271 4.94922V4.16797C7.08271 2.78726 8.202 1.66797 9.58271 1.66797H10.416L10.5446 1.67122C11.8655 1.73824 12.916 2.83041 12.916 4.16797V4.94922Z" fill="white"/>
<path d="M18.2722 1.16258C18.4343 0.721849 19.0574 0.721849 19.2246 1.16258L19.488 1.87686C19.5386 2.01364 19.6501 2.12509 19.7869 2.17575L20.5012 2.43917C20.9419 2.60128 20.9419 3.22438 20.5012 3.39156L19.7869 3.65498C19.6501 3.70564 19.5386 3.81709 19.488 3.95387L19.2246 4.66815C19.0625 5.10888 18.4394 5.10888 18.2722 4.66815L18.0088 3.95387C17.9581 3.81709 17.8466 3.70564 17.7099 3.65498L16.9956 3.39156C16.5549 3.22945 16.5549 2.60635 16.9956 2.43917L17.7099 2.17575C17.8466 2.12509 17.9581 2.01364 18.0088 1.87686L18.2722 1.16258Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.42857 0V1.14286H12.5714V0H16V3.42857H14.8571V12.5714H16V16H12.5714V14.8571H3.42857V16H0V12.5714H1.14286V3.42857H0V0H3.42857ZM2.28571 13.7143H1.14286V14.8571H2.28571V13.7143ZM14.8571 13.7143H13.7143V14.8571H14.8571V13.7143ZM12.5714 2.28571H3.42857V3.42857H2.28571V12.5714H3.42857V13.7143H12.5714V12.5714H13.7143V3.42857H12.5714V2.28571ZM8 4.57143C8.15155 4.57143 8.2969 4.63163 8.40406 4.7388C8.51123 4.84596 8.57143 4.9913 8.57143 5.14286V7.42857H10.8571C11.0087 7.42857 11.154 7.48878 11.2612 7.59594C11.3684 7.7031 11.4286 7.84845 11.4286 8C11.4286 8.15155 11.3684 8.2969 11.2612 8.40406C11.154 8.51123 11.0087 8.57143 10.8571 8.57143H8.57143V10.8571C8.57143 11.0087 8.51123 11.154 8.40406 11.2612C8.2969 11.3684 8.15155 11.4286 8 11.4286C7.84845 11.4286 7.7031 11.3684 7.59594 11.2612C7.48878 11.154 7.42857 11.0087 7.42857 10.8571V8.57143H5.14286C4.9913 8.57143 4.84596 8.51123 4.7388 8.40406C4.63163 8.2969 4.57143 8.15155 4.57143 8C4.57143 7.84845 4.63163 7.7031 4.7388 7.59594C4.84596 7.48878 4.9913 7.42857 5.14286 7.42857H7.42857V5.14286C7.42857 4.9913 7.48878 4.84596 7.59594 4.7388C7.7031 4.63163 7.84845 4.57143 8 4.57143ZM14.8571 1.14286H13.7143V2.28571H14.8571V1.14286ZM2.28571 1.14286H1.14286V2.28571H2.28571V1.14286Z" fill="#0D0D0D"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.42857 0V1.14286H12.5714V0H16V3.42857H14.8571V12.5714H16V16H12.5714V14.8571H3.42857V16H0V12.5714H1.14286V3.42857H0V0H3.42857ZM2.28571 13.7143H1.14286V14.8571H2.28571V13.7143ZM14.8571 13.7143H13.7143V14.8571H14.8571V13.7143ZM12.5714 2.28571H3.42857V3.42857H2.28571V12.5714H3.42857V13.7143H12.5714V12.5714H13.7143V3.42857H12.5714V2.28571ZM10.8571 7.42857C11.0087 7.42857 11.154 7.48878 11.2612 7.59594C11.3684 7.7031 11.4286 7.84845 11.4286 8C11.4286 8.15155 11.3684 8.2969 11.2612 8.40406C11.154 8.51123 11.0087 8.57143 10.8571 8.57143H5.14286C4.9913 8.57143 4.84596 8.51123 4.7388 8.40406C4.63163 8.2969 4.57143 8.15155 4.57143 8C4.57143 7.84845 4.63163 7.7031 4.7388 7.59594C4.84596 7.48878 4.9913 7.42857 5.14286 7.42857H10.8571ZM14.8571 1.14286H13.7143V2.28571H14.8571V1.14286ZM2.28571 1.14286H1.14286V2.28571H2.28571V1.14286Z" fill="#0D0D0D"/>
</svg>

After

Width:  |  Height:  |  Size: 962 B

View File

@@ -0,0 +1,4 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.00048 3.33301C9.00048 2.98775 8.73787 2.70359 8.40152 2.6696L8.33381 2.66634H7.66715C7.29896 2.66634 7.00048 2.96482 7.00048 3.33301V5.11296C7.00048 5.35112 6.8734 5.57134 6.66715 5.69043C6.46092 5.8095 6.20673 5.80944 6.00048 5.69043L4.45881 4.80046C4.14019 4.61654 3.73288 4.72564 3.54866 5.0446H3.54801L3.21532 5.62142C3.03109 5.94051 3.14031 6.34826 3.45881 6.53223L4.99983 7.42155C5.20601 7.5406 5.33309 7.76093 5.33316 7.99902C5.33316 8.23709 5.20596 8.45737 4.99983 8.5765L3.45881 9.46712C3.14031 9.65109 3.03109 10.0588 3.21532 10.3779L3.54801 10.9548H3.54866L3.58577 11.012C3.78349 11.2862 4.16011 11.3713 4.45881 11.1989L6.00048 10.3083C6.20669 10.1893 6.46093 10.1892 6.66715 10.3083C6.87339 10.4274 7.00048 10.6476 7.00048 10.8857V12.6663L7.00374 12.7347C7.03796 13.0708 7.32203 13.333 7.66715 13.333H8.33381C8.70196 13.3329 9.00048 13.0345 9.00048 12.6663V10.8857C9.00048 10.6475 9.1275 10.4273 9.33381 10.3083C9.54007 10.1892 9.79426 10.1892 10.0005 10.3083L11.5421 11.1989L11.6027 11.2301C11.9107 11.3685 12.2797 11.2538 12.4523 10.9548L12.7856 10.3779C12.9698 10.0589 12.8605 9.65117 12.5421 9.46712L11.0005 8.5765C10.7943 8.45738 10.6671 8.23713 10.6671 7.99902C10.6672 7.76086 10.7942 7.54058 11.0005 7.42155L12.5421 6.53223L12.5994 6.49512C12.873 6.29748 12.9583 5.92044 12.7856 5.62142L12.4523 5.0446C12.2682 4.72566 11.8608 4.61662 11.5421 4.80046L10.0005 5.69043C9.79426 5.80947 9.54004 5.80943 9.33381 5.69043C9.12755 5.57134 9.00048 5.35113 9.00048 5.11296V3.33301ZM10.3338 3.95801L10.8755 3.64551C11.8322 3.0934 13.0551 3.42161 13.6073 4.37793H13.6066L13.9399 4.95475H13.9406C14.4926 5.91099 14.1654 7.13469 13.2088 7.68717H13.2082L12.6671 7.99902L13.2088 8.31217L13.2967 8.36621C14.1849 8.94119 14.4753 10.1183 13.9406 11.0446H13.9399L13.6073 11.6214C13.0551 12.5777 11.8322 12.906 10.8755 12.3538L10.3338 12.0407V12.6663C10.3338 13.7364 9.49335 14.6101 8.43668 14.6637L8.33381 14.6663H7.66715C6.56258 14.6663 5.66715 13.7709 5.66715 12.6663V12.0407L5.12548 12.3538C4.19859 12.8888 3.02167 12.5976 2.44709 11.7093L2.39371 11.6214L2.06038 11.0446C1.50835 10.0884 1.83501 8.86475 2.7915 8.31217L3.33251 7.99902L2.79215 7.68717C1.83545 7.13468 1.50829 5.911 2.06038 4.95475L2.39371 4.37793L2.44709 4.29004C3.02167 3.40179 4.1986 3.1105 5.12548 3.64551L5.66715 3.95801V3.33301C5.66715 2.22844 6.56258 1.33301 7.66715 1.33301H8.33381L8.43668 1.33561C9.49335 1.38923 10.3338 2.26296 10.3338 3.33301V3.95801Z" fill="#0D0D0D"/>
<path d="M14.6187 0.93143C14.7484 0.578846 15.2469 0.578846 15.3806 0.93143L15.5914 1.50286C15.6319 1.61228 15.7211 1.70144 15.8305 1.74197L16.4019 1.95271C16.7545 2.08239 16.7545 2.58087 16.4019 2.71461L15.8305 2.92535C15.7211 2.96588 15.6319 3.05504 15.5914 3.16446L15.3806 3.73589C15.2509 4.08847 14.7525 4.08847 14.6187 3.73589L14.408 3.16446C14.3675 3.05504 14.2783 2.96588 14.1689 2.92535L13.5974 2.71461C13.2449 2.58493 13.2449 2.08644 13.5974 1.95271L14.1689 1.74197C14.2783 1.70144 14.3675 1.61228 14.408 1.50286L14.6187 0.93143Z" fill="#FF7A51"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,4 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.2494 4.16797C11.2494 3.73639 10.9211 3.3812 10.5007 3.3387L10.416 3.33464H9.58271C9.12248 3.33464 8.74938 3.70773 8.74938 4.16797V6.3929C8.74938 6.69061 8.59052 6.96588 8.33271 7.11475C8.07493 7.26358 7.75719 7.26351 7.49938 7.11475L5.5723 6.00228C5.17401 5.77239 4.66488 5.90876 4.4346 6.30745H4.43379L4.01793 7.02848C3.78765 7.42735 3.92416 7.93703 4.3223 8.16699L6.24857 9.27865C6.50629 9.42746 6.66514 9.70287 6.66523 10.0005C6.66523 10.2981 6.50622 10.5734 6.24857 10.7223L4.3223 11.8356C3.92416 12.0656 3.78764 12.5752 4.01793 12.9741L4.43379 13.6951H4.4346L4.48099 13.7668C4.72814 14.1094 5.19891 14.2158 5.5723 14.0003L7.49938 12.887C7.75714 12.7383 8.07495 12.7383 8.33271 12.887C8.59052 13.0359 8.74938 13.3112 8.74938 13.6089V15.8346L8.75345 15.9201C8.79623 16.3402 9.15132 16.668 9.58271 16.668H10.416C10.8762 16.6679 11.2494 16.2948 11.2494 15.8346V13.6089C11.2494 13.3111 11.4082 13.0359 11.666 12.887C11.9239 12.7383 12.2416 12.7382 12.4994 12.887L14.4265 14.0003L14.5021 14.0394C14.8872 14.2123 15.3484 14.0689 15.5642 13.6951L15.9808 12.9741C16.211 12.5754 16.0744 12.0657 15.6765 11.8356L13.7494 10.7223C13.4916 10.5734 13.3327 10.2981 13.3327 10.0005C13.3328 9.70279 13.4915 9.42743 13.7494 9.27865L15.6765 8.16699L15.7481 8.12061C16.0901 7.87355 16.1966 7.40226 15.9808 7.02848L15.5642 6.30745C15.334 5.90878 14.8248 5.77249 14.4265 6.00228L12.4994 7.11475C12.2416 7.26355 11.9238 7.2635 11.666 7.11475C11.4082 6.96588 11.2494 6.69062 11.2494 6.3929V4.16797ZM12.916 4.94922L13.5931 4.55859C14.789 3.86845 16.3177 4.27872 17.0078 5.47412H17.007L17.4237 6.19515H17.4245C18.1145 7.39045 17.7055 8.92007 16.5098 9.61068H16.509L15.8327 10.0005L16.5098 10.3919L16.6197 10.4595C17.7299 11.1782 18.0929 12.6496 17.4245 13.8075H17.4237L17.0078 14.5285C16.3177 15.7239 14.789 16.1342 13.5931 15.444L12.916 15.0526V15.8346C12.916 17.1722 11.8655 18.2644 10.5446 18.3314L10.416 18.3346H9.58271C8.202 18.3346 7.08271 17.2153 7.08271 15.8346V15.0526L6.40563 15.444C5.24702 16.1128 3.77587 15.7487 3.05765 14.6383L2.99092 14.5285L2.57425 13.8075C1.88421 12.6123 2.29255 11.0826 3.48815 10.3919L4.16442 10.0005L3.48896 9.61068C2.2931 8.92005 1.88414 7.39045 2.57425 6.19515L2.99092 5.47412L3.05765 5.36426C3.77587 4.25395 5.24703 3.88984 6.40563 4.55859L7.08271 4.94922V4.16797C7.08271 2.78726 8.202 1.66797 9.58271 1.66797H10.416L10.5446 1.67122C11.8655 1.73824 12.916 2.83041 12.916 4.16797V4.94922Z" fill="white"/>
<path d="M18.2722 1.16258C18.4343 0.721849 19.0574 0.721849 19.2246 1.16258L19.488 1.87686C19.5386 2.01364 19.6501 2.12509 19.7869 2.17575L20.5012 2.43917C20.9419 2.60128 20.9419 3.22438 20.5012 3.39156L19.7869 3.65498C19.6501 3.70564 19.5386 3.81709 19.488 3.95387L19.2246 4.66815C19.0625 5.10888 18.4394 5.10888 18.2722 4.66815L18.0088 3.95387C17.9581 3.81709 17.8466 3.70564 17.7099 3.65498L16.9956 3.39156C16.5549 3.22945 16.5549 2.60635 16.9956 2.43917L17.7099 2.17575C17.8466 2.12509 17.9581 2.01364 18.0088 1.87686L18.2722 1.16258Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,76 @@
<template>
<transition name="fade">
<div v-if="show" class="ai-selectbox-panel">
<div>
<span class="icon"><svg-icon name="dc-add" size="16" /></span>
<span class="label">Add</span>
</div>
<div>
<span class="icon"><svg-icon name="dc-remove" size="16" /></span>
<span class="label">Remove</span>
</div>
<button>创建</button>
</div>
</transition>
</template>
<script setup lang="ts">
import { ref, inject, computed, watch } from 'vue'
import depthSlider from './tools/depth-slider.vue'
import { OperationType } from '../tools/layerHelper'
const props = defineProps({
currentTool: { required: true, type: [String, null] }
})
const stateManager = inject('stateManager') as any
const toolManager = inject('toolManager') as any
const showTools = [OperationType.SELECTBOX]
const show = computed(() => showTools.includes(props.currentTool))
</script>
<style lang="less" scoped>
// 淡入淡出动画
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.ai-selectbox-panel {
position: absolute;
top: 8.8rem;
left: 50%;
transform: translateX(-50%);
height: 4.1rem;
padding: 0 1.7rem;
border: 0.2rem solid #ebebeb;
background: #ffffff;
border-radius: 1.1rem;
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;
> div {
display: flex;
cursor: pointer;
align-items: center;
justify-content: center;
height: 2.6rem;
padding: 0 0.7rem;
border-radius: 0.3rem;
gap: 1rem;
> .label {
font-size: 1.4rem;
color: #000;
}
&.active,
&:hover {
background: rgba(235, 235, 235, 0.9);
}
}
}
</style>

View File

@@ -178,8 +178,8 @@
}
const onWorkbench = async () => {
exportCanvasToImage(canvasManager.canvas).then((url) => {
const json = canvasManager.getCanvasJSON()
emit('workbench', { url, json })
const { canvas, images } = canvasManager.getCanvasDisUrlJSON()
emit('workbench', { url, canvas, images })
})
}
</script>

View File

@@ -6,8 +6,10 @@
<template v-if="isReady">
<layer-panel />
<details-panel />
<depth-header-tools @export="exportCanvas" @workbench="(v) => emit('workbench', v)" />
<brush-control-panel :currentTool="toolManager.currentTool.value" />
<ai-selectbox-panel :currentTool="toolManager.currentTool.value" />
<depth-header-tools @export="exportCanvas" @workbench="(v) => emit('workbench', v)" />
<zoom
:zoom="canvasManager.currentZoom.value / 100"
is-home
@@ -30,6 +32,7 @@
import depthHeaderTools from './components/depth-header-tools.vue'
import zoom from '../components/zoom.vue'
import brushControlPanel from './components/brush-control-panel.vue'
import aiSelectboxPanel from './components/ai-selectbox-panel.vue'
// 管理器
import { StateManager } from './manager/StateManager'
@@ -60,8 +63,8 @@
provide('stateManager', stateManager)
// 画布管理器
const canvasManager = new CanvasManager({ stateManager })
stateManager.setManager({ canvasManager, canvasRef })
const canvasManager = new CanvasManager({ stateManager, props })
stateManager.setManager({ canvasManager })
provide('canvasManager', canvasManager)
// 图层管理器
@@ -89,16 +92,19 @@
globalStore.setLoading(true)
keyEventManager.registerEvents()
const url = props.config.url || ''
const json = props.config.json || ''
const canvasData = props.config.canvasData || ''
await canvasManager.initCanvas({
canvasRef,
canvasViewWidth: canvasContainerRef.value.clientWidth,
canvasViewHeight: canvasContainerRef.value.clientHeight,
canvasWidth: props.config.width || 750,
canvasHeight: props.config.height || 600,
url: json ? '' : url
url: canvasData ? '' : url
})
if (json) await canvasManager.loadJSON(json)
if (canvasData) {
const json = canvasManager.processCanvasDisUrlJSON(canvasData)
await canvasManager.loadJSON(json)
}
globalStore.setLoading(false)
stateManager.onMounted()

View File

@@ -9,33 +9,54 @@
<script setup lang="ts">
import { base64Tofile } from '../tools/tools'
import { uploadImage } from '@/api/upload'
import { getDepthCanvas, saveDepthCanvas } from '@/api/depth-canvas'
import FullscreenDialog from '../components/fullscreen-dialog.vue'
import depthCanvas from './depth-canvas.vue'
import { ref } from 'vue'
const dialogVisible = ref(false)
const config = ref({
id: '',
canvasId: '',
sketchId: '',
url: '',
json: ''
canvasData: '' as any,
onWorkbench(options) {},
onClose() {}
})
const open = (options) => {
const open = async (options) => {
config.value = options
console.log(config.value)
config.value.json = sessionStorage.getItem('canvasJson_' + config.value.id)
if (config.value.canvasId) {
const res = await getDepthCanvas(config.value.canvasId)
const canvas = res.deepCanvasDetail
const images = res.images
if (canvas && images) {
config.value.canvasData = { canvas: JSON.stringify(canvas), images }
} else {
config.value.canvasData = null
}
}
dialogVisible.value = true
}
// 工作区
const onWorkbench = async (options) => {
const json = options.json
sessionStorage.setItem('canvasJson_' + config.value.id, json)
const onWorkbench = async (options: { canvas: string; images: Object; url: string }) => {
// 保存画布数据
const data = {
deepCanvasDetail: JSON.parse(options.canvas),
images: options.images,
sketchId: config.value.sketchId,
id: config.value.canvasId || null
}
const sData = await saveDepthCanvas(data)
const canvasId = sData.id
// base64 转 file 上传转换为 url
const file = base64Tofile(options.url, 'canvas.png')
const formData = new FormData()
formData.append('file', file)
const url = await uploadImage(formData)
config.value.onWorkbench?.({ url })
const url = await uploadImage(formData, true)
config.value.onWorkbench?.({ url, canvasId })
dialogVisible.value = false
}
// 关闭

View File

@@ -1,4 +1,6 @@
import { fabric } from 'fabric-with-all'
import { createId } from '../../tools/tools'
/** 智能框选工具管理器 */
export class AISelectboxToolManager {
// 管理器
@@ -14,6 +16,7 @@ export class AISelectboxToolManager {
this.canvasManager = options.canvasManager
this.stateManager = options.stateManager
this.layerManager = options.layerManager
}
mouseDownEvent(e) {
this.isDragging = true
@@ -39,11 +42,11 @@ export class AISelectboxToolManager {
var height = e.absolutePointer.y - this.startY
var left = this.startX
var top = this.startY
if(width < 0) {
if (width < 0) {
left += width
width = -width
}
if(height < 0) {
if (height < 0) {
top += height
height = -height
}
@@ -60,6 +63,249 @@ export class AISelectboxToolManager {
this.canvasManager.canvas.remove(this.demoObject)
this.canvasManager.canvas.renderAll()
this.createSelectbox()
}
loadImageToObject(url) {
return new Promise((resolve, reject) => {
fabric.Image.fromURL(url, (img) => {
resolve(img);
}, { crossOrigin: "anonymous" });// 防止污染
});
}
rgba = { r: 0, g: 255, b: 0, a: 200 };
selectionStyle = {
stroke: "rgba(255, 77, 71, 1)",
strokeWidth: 1.5,
strokeDashArray: [4, 4],
fill: "rgba(255, 186, 186, 0.5)",
strokeUniform: true, // 保持描边宽度不随缩放改变
// strokeLineCap: "round",// 折线端点样式
// strokeLineJoin: "bevel", // 折线连接样式
// selectable: false,
// evented: false,
excludeFromExport: true,
hoverCursor: "default",
moveCursor: "default",
};
async createSelectbox() {
const url = "http://118.31.39.42:3000/falls/1a48ed3a-1faa-4fcd-bf07-765dba1702c5.png"
const image = await this.loadImageToObject(url)
const canvas = getObjectAlphaToCanvas(image, null, 0, this.rgba);
const fobject = this.canvasManager.canvas.clipPath
// const top = fobject.top - fobject.height * scaleY / 2;
// const left = fobject.left - fobject.width * scaleX / 2;
const scaleY = fobject.scaleY
const scaleX = fobject.scaleX
const top = fobject.top
const left = fobject.left
const arr = traceImageContour(canvas);
let minX = fobject.width;
let minY = fobject.height;
const str = arr.map((v) => {
if (v.x < minX) minX = v.x;
if (v.y < minY) minY = v.y;
return `${v.x} ${v.y}`
}).join(" L ");
const path = new fabric.Path(`M ${str} z`);
path.set({
left: left + minX * scaleX,
top: top + minY * scaleY,
scaleX: scaleX,
scaleY: scaleY,
...this.selectionStyle,
});
const rect1 = new fabric.Rect({
left: 0,
top: 0,
width: 100,
height: 100,
fill: '#f00',
info: {
id: createId("rect"),
name: '矩形图层',
}
})
const rect2 = new fabric.Rect({
left: 200,
top: 200,
width: 100,
height: 100,
fill: '#ff0',
info: {
id: createId("rect"),
name: '矩形图层',
}
})
this.layerManager.createGroupLayer({
child: [rect1, rect2],
})
// this.canvasManager.canvas.add(path)
// this.canvasManager.canvas.renderAll()
}
dispose() { }
}
/**
* 获取对象黑白通道画布
* @param {fabric.Object} object - 要处理的 fabric 对象
* @param {ImageData} revData - 相反的ImageData白通道的相同位置是否为透明revData为白色为透明黑色为不透明
* @param {number} diff - 差值,默认 25
* @param {Object} rgba - 自定义 rgba 值,默认 { r: 255, g: 255, b: 255, a: 255 }
* @param {boolean} isMerge - 是否合并true=合并revDatafalse=反转revData
* @returns {HTMLCanvasElement|null} 包含黑白通道的画布,或 null 如果失败
*/
export function getObjectAlphaToCanvas(object, revData, diff = 30, rgba = { r: 255, g: 255, b: 255, a: 255 }, isMerge = false) {
const image = object.getElement();
if (image.nodeName !== "IMG" && image.nodeName !== "CANVAS") {
console.warn("对象不是图片");
return null;
}
const { width, height } = image;
if (!width || !height) {
console.warn("对象没有元素");
return null;
}
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, width, height);
const data = ctx.getImageData(0, 0, width, height);
for (let i = 0; i < data.data.length; i += 4) {
const r = data.data[i + 0];
const g = data.data[i + 1];
const b = data.data[i + 2];
const a = data.data[i + 3];
const revR = revData?.data[i + 0] || 0;
const revG = revData?.data[i + 1] || 0;
const revB = revData?.data[i + 2] || 0;
const revA = revData?.data[i + 3] || 0;
let isHave = false;
if (r || g || b || a) {
if (revR > diff || revG > diff || revB > diff || revA > diff) {
isHave = false;
} else {
isHave = true;
}
}
if (isMerge && (revR || revG || revB || revA)) isHave = true;
if (isHave) {
data.data[i + 0] = rgba.r;
data.data[i + 1] = rgba.g;
data.data[i + 2] = rgba.b;
data.data[i + 3] = rgba.a;
} else {
data.data[i + 0] = 0;
data.data[i + 1] = 0;
data.data[i + 2] = 0;
data.data[i + 3] = 0;
}
}
ctx.clearRect(0, 0, width, height);
ctx.putImageData(data, 0, 0);
return canvas;
}
/**
* 图片边界跟踪算法(透明底)
* @param {HTMLCanvasElement} canvas - canvas元素
* @param {Number} scale - 缩放比例
* @returns {Array} 边界点数组 [{x, y}, ...]
*/
export function traceImageContour(canvas) {
const ctx = canvas.getContext("2d", { willReadFrequently: true });
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const width = canvas.width;
const height = canvas.height;
// 查找起始点(第一个不透明像素)
let startX = -1;
let startY = -1;
outer: for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const index = (y * width + x) * 4;
if (data[index + 3] > 0) {
startX = x;
startY = y;
break outer;
}
}
}
if (startX === -1) return []; // 没有不透明像素
// Moore-Neighbor边界跟踪算法
const contour = [];
const visited = new Set();
const directions = [
[-1, 0],
[-1, -1],
[0, -1],
[1, -1],
[1, 0],
[1, 1],
[0, 1],
[-1, 1],
];
let currentX = startX;
let currentY = startY;
let backtrackDir = 4; // 起始方向:右
do {
const pointKey = `${currentX},${currentY}`;
if (!visited.has(pointKey)) {
contour.push({ x: currentX, y: currentY });
visited.add(pointKey);
}
// 从右方向开始顺时针查找
let found = false;
for (let i = 0; i < 8; i++) {
const dir = (backtrackDir + i) % 8;
const dx = directions[dir][0];
const dy = directions[dir][1];
const checkX = currentX + dx;
const checkY = currentY + dy;
if (
checkX >= 0 &&
checkX < width &&
checkY >= 0 &&
checkY < height
) {
const index = (checkY * width + checkX) * 4;
if (data[index + 3] > 0) {
currentX = checkX;
currentY = checkY;
backtrackDir = (dir + 5) % 8; // 下一个开始查找的方向
found = true;
break;
}
}
}
if (!found) break;
} while (
!(currentX === startX && currentY === startY) &&
visited.size < width * height
);
return contour;
}

View File

@@ -6,6 +6,7 @@ import { detectDeviceType } from '../tools/index'
import { CanvasEventManager } from "./events/CanvasEventManager";
import { OperationType } from '../tools/layerHelper'
import { createId } from '../../tools/tools'
import md5 from 'md5'
// 自定义画布转对象属性
fabric.Object.prototype.customProperties = ["top", "left", "width", "height", "scaleX", "scaleY", "info", "thumbnail"];
@@ -45,8 +46,10 @@ export class CanvasManager {
deviceInfo: any
canvas: any
currentZoom: any
uniqueId: string
constructor(options) {
this.stateManager = options.stateManager;
this.uniqueId = md5(options.props.config.url || Date.now());
this.deviceInfo = detectDeviceType();
this.currentZoom = ref(100)
}
@@ -279,6 +282,7 @@ export class CanvasManager {
console.error('JSON解析错误:', error)
}
if (!jsonObj) return resolve(false)
if (jsonObj.uniqueId) this.uniqueId = jsonObj.uniqueId
this.canvas.loadFromJSON(jsonObj, () => {
if (rerecord) this.stateManager.clearState()
this.resetZoom(false)
@@ -293,35 +297,39 @@ export class CanvasManager {
/** 导出画布为处理图片的JSON */
getCanvasDisUrlJSON() {
const canvas = this.canvas.toJSON()
const images = [];
var i = 0;
const create = (url) => {
const logo = `xxxxxxxx_${i++}_xxxxxxxx`;
images.push({ index: logo, url })
return logo
const images = {};
const create = (url, key) => {
const key_ = `xxxxx_${this.uniqueId}_${md5(key)}_xxxxx`;
images[key_] = url
return key_
}
canvas.uniqueId = this.uniqueId
canvas.objects.forEach((object: any) => {
const id = object.info?.id
if (object.thumbnail) {
object.thumbnail = create(object.thumbnail)
object.thumbnail = create(object.thumbnail, `thumbnail_${id}`)
}
if (object.src) {
object.src = create(object.src)
object.src = create(object.src, `src_${id}`)
object.crossOrigin = 'anonymous'
}
if (object.fill?.source) {
object.fill.source = create(object.fill.source)
object.fill.source = create(object.fill.source, `fillsource_${id}`)
}
if (object.info?.fill?.source) {
object.info.fill.source = create(object.info.fill.source)
object.info.fill.source = create(object.info.fill.source, `infofillsource_${id}`)
}
})
return { canvas: JSON.stringify(canvas), images }
}
/** 处理JSON为正常画布 */
processCanvasDisUrlJSON(obj: { canvas: string, images: Object[] }) {
processCanvasDisUrlJSON(obj: { canvas: string, images: Object }) {
var json = obj.canvas;
const images = obj.images || []
images.forEach((v: any) => json = json.replace(new RegExp(v.index), v.url))
return JSON.parse(json)
const images = obj.images || {}
for (const key in images) {
json = json.replace(new RegExp(key), images[key])
}
return json
}
dispose() {
this.animationManager?.dispose()

View File

@@ -151,6 +151,37 @@ export class LayerManager {
if (isActive) this.setActiveID(emptyObject.info.id, false)
return emptyObject
}
/** 创建组图层 */
createGroupLayer(options?: any, isRecord = true, isActive = false) {
const child = options?.child || []
delete options.child
const groupObject = new fabric.Group(child, {
// subTargetCheck: true, // 关键:检测子对象
// interactive: true, // 启用交互
// hasControls: true,
// hasBorders: true,
// // 子对象样式
// cornerColor: 'blue',
// cornerSize: 8,
// borderColor: 'green',
// // 允许子对象独立变换
// lockScalingX: false,
// lockScalingY: false,
// lockRotation: false,
info: {
id: createId("group"),
name: '组图层',
...(options?.info || {}),
}
})
// this.setLayerPosition(groupObject)
this.canvasManager.add(groupObject, isRecord)
if (isActive) this.setActiveID(groupObject.info.id, false)
return groupObject
}
/** 创建文本图层 */
async createTextLayer(text: string, options?: any) {
const textObject = new fabric.IText(text, {
@@ -337,7 +368,7 @@ export class LayerManager {
}
})
resolve(img)
})
}, { crossOrigin: 'anonymous' })
})
// console.log(mergedImage)
const index = this.canvasManager.getObjects().indexOf(targetLayer);

View File

@@ -53,7 +53,7 @@
const isSubord = computed(() => nodes.value.some((v) => v.data.superiorID === props.node.id))
const tier = computed(() => Number(props.node?.data?.tier || 0))
//只有3d模型才有三级菜单,目前三级菜单内容少直接禁用按钮
const isAdd3d = computed(() => (tier.value === 2 && props.node?.data?.superiorNodeType === NODE_DATATYPE.TO_3D_MODEL) || tier.value != 2)
const isAdd3d = computed(() => (tier.value === 2 && props.node?.data?.superiorNodeType === NODE_DATATYPE.TO_3D_MODEL) || props.node?.data?.superiorNodeType !== NODE_DATATYPE.TO_3D_MODEL)
const isReturned = computed(() => {
return (
props.node.data.type == NODE_DATATYPE.RESULT_IMAGE &&
@@ -79,6 +79,7 @@
props.stateManager.nodeManager.createCardsSelect({
data: { tier: tier_, superiorID: props.node.id, originalImage }
})
stateManager.setActiveNodeID('')
}
const posCenter = computed(() => {
const arr = [NODE_DATATYPE.RESULT_IMAGE]

View File

@@ -1,7 +1,7 @@
<template>
<!-- 高级工具选择 -->
<div class="cards-select">
<div v-for="v in list" :key="v.type" @click="onClickItem(v)" v-show="v.tier === tier">
<div v-for="v in (node.data?.secondaryMenu?.selectList || list)" :key="v.type" @click="onClickItem(v)" v-show="v.tier === tier">
<span class="icon">
<svg-icon :name="v.type + '-2'" size="15" size-unit="px" />
</span>
@@ -28,7 +28,22 @@
{
tier: NODE_DATATIER.SURFACE_EDIT,
type: NODE_DATATYPE.SURFACE_EDIT,
title: 'Surface Edit'
title: 'Surface Edit',
secondaryMenu: {
title: 'Surface Edit',
icon: NODE_DATATYPE.SURFACE_EDIT,
selectList: [
{
tier: NODE_DATATIER.CANVAS_MODE,
type: NODE_DATATYPE.CANVAS_MODE,
title: 'Surface Edit (Canvas)',
},{
tier: NODE_DATATIER.Fast_MODE,
type: NODE_DATATYPE.Fast_MODE,
title: 'Surface Edit',
},
]
}
},
{
tier: NODE_DATATIER.SCENE_COMPOSITION,
@@ -55,6 +70,17 @@
const id = props.node.id
if (!id) return
stateManager.deleteNode(id)
console.log(props.node)
if(v.secondaryMenu){
nodeManager.createCardsSelect({
data: {
tier: props.node.data?.tier,
superiorID: props.node.data?.superiorID,
originalImage: props.node.data?.originalImage,
secondaryMenu: v.secondaryMenu
}
})
}else{
const superiorID = props.node.data.superiorID
nodeManager.createCardNode({
data: {
@@ -65,6 +91,7 @@
}
})
}
}
defineExpose({})
</script>

View File

@@ -0,0 +1,77 @@
<template>
<!-- 画布编辑印花 -->
<div class="fast-mode">
<p class="label">Output</p>
<div class="imgBox">
<img :src="data.url" alt="">
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, inject, useAttrs } from 'vue'
import myEvent from '@/utils/myEvent'
import { getCurrentTime } from '../../../../tools/tools.ts'
import { NODE_DATATIER } from '../../../tools/index.d'
const attrs = useAttrs()
const data = reactive({
url: attrs.node?.data?.originalImage,
})
const stateManager = inject('stateManager') as any
const nodeManager = inject('nodeManager') as any
const eventManager = inject('eventManager') as any
const getApiData = ()=>{
return {
}
}
const opCanvas = ()=>{
const data = {
url:attrs?.node?.data?.originalImage,
canvasId: attrs?.node?.data?.canvasId || null,
sketchId: stateManager.sketchId.value,
onWorkbench:(options)=>{
let taskId = Date.now()
let workbenchData = {
createTime: getCurrentTime(),
status: 'RETURNED',
taskId: taskId + '',
...options,
}
const subordNodes = stateManager.getSubordNodes(attrs.node.id)
nodeManager.createResultNode({
data: {
superiorID: attrs.node.id,
superiorNodeType: attrs.node?.data?.type,
createIndexPosition: 0 + subordNodes.length,
tier: NODE_DATATIER.RESULT_IMAGE,
isActive: subordNodes.length == 0,
data: {
imageProcessTasks:[workbenchData],
selectTaskId:taskId + '',
}
}
})
},
}
eventManager.removeEvents()
myEvent.emit('openDepthCanvas', data)
}
defineExpose({ data, getApiData, opCanvas })
</script>
<style lang="less" scoped>
.fast-mode {
margin-bottom: 40px;
> .imgBox{
width: 217px;
height: 217px;
padding: 7.5px;
> img{
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
</style>

View File

@@ -2,13 +2,13 @@
<div class="card">
<div class="header">
<svg-icon
v-if="!currentComponent?.hideIcon"
:name="currentComponent?.type"
v-if="attrs.node?.data?.secondaryMenu?.icon || !currentComponent?.hideIcon"
:name="attrs.node?.data?.secondaryMenu?.icon || currentComponent?.type"
color="#fff"
size="16"
size-unit="px"
/>
<span>{{ currentComponent?.title }}</span>
<span>{{ attrs.node?.data?.secondaryMenu?.title ||currentComponent?.title }}</span>
<div class="delete-icon" @click="onDeleteClick">
<SvgIcon name="flowDelete" size="16" size-unit="px" color="#fff" />
</div>
@@ -22,6 +22,12 @@
<span>Generate</span>
</button>
</div>
<div class="footer canvasEdit" @mousedown.stop v-if="currentComponent?.showCanvasEdit">
<button @click="currentComponent?.on()">
<svg-icon name="xingxing" size="16" size-unit="px" />
<span>Edit</span>
</button>
</div>
</div>
</template>
@@ -31,6 +37,7 @@
import CardsSelect from './cards-select.vue'
import ToRealStyle from './to-real-style.vue'
import SurfaceEdit from './surface-edit.vue'
import FastMode from './fast-mode.vue'
import SceneComposition from './scene-composition.vue'
import ColorPalette from './color-palette.vue'
import To3View from './to-3view.vue'
@@ -41,6 +48,8 @@
// import ToVideo from './to-video.vue'
// import AddPrint from './add-print.vue'
// import ToCAD from './to-cad.vue'
const attrs = useAttrs()
const componentRef = ref(null)
const components = [
{
tier: NODE_DATATIER.CARDS_SELECT,
@@ -58,12 +67,23 @@
api: toRealStyleApi
},
{
tier: NODE_DATATIER.SURFACE_EDIT,
type: NODE_DATATYPE.SURFACE_EDIT,
tier: NODE_DATATIER.Fast_MODE,
type: NODE_DATATYPE.Fast_MODE,
title: 'Surface Edit',
component: SurfaceEdit,
api: sketchAddPrintApi
},
{
tier: NODE_DATATIER.CANVAS_MODE,
type: NODE_DATATYPE.CANVAS_MODE,
title: 'Surface Edit (Canvas)',
component: FastMode,
hideFooter: true,
showCanvasEdit: true,
on: ()=>{
componentRef.value?.opCanvas()
}
},
{
tier: NODE_DATATIER.SCENE_COMPOSITION,
type: NODE_DATATYPE.SCENE_COMPOSITION,
@@ -117,11 +137,9 @@
default: () => ({})
}
})
const attrs = useAttrs()
const currentComponent = computed(() => {
return components.find((item) => item.type === props.type)
})
const componentRef = ref(null)
const onGenerateClick = async () => {
const data = componentRef.value?.getApiData?.() || {}
@@ -140,13 +158,16 @@
}) || []
// const taskList = [{taskId:'123'}]
// if (!subordNode) {
//如果是添加印花的结果就作为一级节点可以再次选择添加印花或者生成真实图
let tier = (NODE_DATATIER.Fast_MODE == currentComponent.value.tier && currentComponent.value.type == NODE_DATATYPE.Fast_MODE)?0:currentComponent.value.tier
taskList.forEach((item,index) => {
nodeManager.createResultNode({
data: {
superiorID: attrs.node.id,
superiorNodeType: attrs.node?.data?.type,
createIndexPosition: index + subordNodes.length,
tier: currentComponent.value.tier,
tier: tier,
isActive: index == 0 && subordNodes.length == 0,
data: {
imageProcessTasks:[item],
@@ -251,6 +272,7 @@
font-size: 12px;
font-family: SemiBold;
cursor: pointer;
&:active {
opacity: 0.5;
}
@@ -260,6 +282,12 @@
margin-right: 4px;
}
}
&.canvasEdit{
> button{
margin: 0 8px;
width: 201px;
}
}
}
}
</style>

View File

@@ -4,10 +4,10 @@
<div class="result-image"
v-for="(item, i) in data.imageProcessTasks"
:key="item.taskId"
:class="{'active': config.isActive}"
:class="{'active': node.id == stateManager.activeNodeID.value}"
@click="setSelectTaskId(item.taskId)"
>
<div class="header" v-if="item.status == 'RETURNED'" v-show="showHeader && config.isActive" @mousedown.stop>
<div class="header" v-if="item.status == 'RETURNED'" v-show="showHeader && node.id == stateManager.activeNodeID.value" @mousedown.stop>
<span class="icon">
<svg-icon name="chat-compose" size="20" size-unit="px" />
</span>
@@ -23,7 +23,6 @@
</button>
</div>
<img
v-loadimg="false"
class="image"
v-if="item.status == 'RETURNED'"
:src="item?.url"
@@ -59,11 +58,10 @@
<script setup lang="ts">
import myEvent from '@/utils/myEvent'
import { downloadImage, base64Tofile } from '../../../tools/tools'
import { downloadImage } from '../../../tools/tools'
import { reactive, ref, onBeforeUnmount, useAttrs, inject, watch, computed, onMounted } from 'vue'
import HighlightAdmin from '@/components/highlightAdmin.vue'
import { NODE_DATATYPE } from '../../tools/index.d'
import { uploadImage } from '@/api/upload'
const openImagePreview = inject('openImagePreview') as (url: string) => void
const openThreeModelPreview = inject('openThreeModelPreview') as (url: string) => void
const props = defineProps({
@@ -88,7 +86,8 @@
'update-data'
])
// const attrs = useAttrs()
const showHeader = ref(!!props.node?.data?.isHeader)
const showHeader = ref(true)
// const showHeader = ref(!!props.node?.data?.isHeader)
const showMenu = ref(false)
const clickTaskId = ref('')
const generateManager = inject('generateManager') as any
@@ -183,7 +182,7 @@
])
const onPreview = (item: any) => {
if(data.superiorNodeType == NODE_DATATYPE.TO_3D_MODEL){
openThreeModelPreview({url:item?.glbPath,glbInfoObj:item?.glbInfoObj})
openThreeModelPreview({glbPath:item?.glbPath,glbInfoObj:item?.glbInfoObj})
}else{
openImagePreview(item.url)
}
@@ -205,25 +204,19 @@
eventManager.registerEvents()
}
const depthCanvasWorkbench = (options)=>{
console.log(options)
// 1. 提取 MIME 类型和 Base64 数据
const file = base64Tofile(options.url,'image.png')
const formData = new FormData()
formData.append('file', file)
uploadImage(formData).then((res) => {
data.imageProcessTasks.forEach((item) => {
if(item.taskId == options.taskId){
item.url = res
item.url = options.url
item.canvasId = options.canvasId || null
}
})
})
}
const onEdit = (item: any) => {
const data = {
url:item.url,
id: item.taskId,
canvasId: item?.canvasId || null,
sketchId: stateManager.sketchId.value,
onWorkbench:(options)=>{
let workbenchData = {
...item,

View File

@@ -18,8 +18,7 @@ let data = reactive({
const modelRef = ref(null)
onMounted(()=>{
console.log(props?.currentData?.url)
if(props?.currentData?.url)modelRef.value.open(props?.currentData?.url)
if(props?.currentData?.glbPath)modelRef.value.open(props?.currentData?.glbPath)
})
onUnmounted(()=>{
})

View File

@@ -8,7 +8,7 @@
import FullscreenDialog from '../components/fullscreen-dialog.vue'
import flowCanvas from './flow-canvas.vue'
import { ref } from 'vue'
import { getSketchFlowCanvas, putSketchFlowCanvas } from '@/api/flow-canvas'
import { getSketchFlowCanvas } from '@/api/flow-canvas'
import { useI18n } from 'vue-i18n'
const dialogVisible = ref(false)

View File

@@ -63,8 +63,7 @@ export class StateManager {
this.mxHistory = ref(50)
this.historyList = ref([])
this.historyIndex = ref(0)
this.sketchId = options.sketchId
this.sketchId = ref(options.sketchId)
this.saveCanvasTimeInterval = 6000
this.saveCanvasTime = null
@@ -188,12 +187,12 @@ export class StateManager {
}
/** 画布数据存储 */
async exportFlow (time:number = 0){
if(!this.sketchId)return
if(!this.sketchId.value)return
clearTimeout(this.saveCanvasTime)
await new Promise((resolve) => {
this.saveCanvasTime = setTimeout(()=>{
putSketchFlowCanvas({
id: this.sketchId,
id: this.sketchId.value,
canvasData: JSON.stringify(this.nodes.value) }).then(() => {
resolve(true)
}).catch(() => {

View File

@@ -25,6 +25,8 @@ export const NODE_DATATYPE = {
CARDS_SELECT: 'cards-select',
TO_REAL_STYLE: 'to-real-style',
SURFACE_EDIT: 'surface-edit',
CANVAS_MODE: 'canvas-mode',
Fast_MODE: 'fast-mode',
SCENE_COMPOSITION: 'scene-composition',
COLOR_PALETTE: 'color-palette',
TO_3D_MODEL: 'to-3d-model',
@@ -38,6 +40,8 @@ export const NODE_DATATIER = {
CARDS_SELECT: 0,
TO_REAL_STYLE: 1,
SURFACE_EDIT: 1,
CANVAS_MODE: 1,
Fast_MODE: 1,
SCENE_COMPOSITION: 2,
COLOR_PALETTE: 2,
TO_3D_MODEL: 2,

View File

@@ -78,3 +78,15 @@ export const base64Tofile = (base64: string,name: string) => {
const file = new File([blob], name, { type: mime })
return file
}
//获取当前时间2026-03-20 11:38:29
export const getCurrentTime = () => {
const now = new Date()
const currentTime =
now.getFullYear() + '-' +
String(now.getMonth() + 1).padStart(2, '0') + '-' +
String(now.getDate()).padStart(2, '0') + ' ' +
String(now.getHours()).padStart(2, '0') + ':' +
String(now.getMinutes()).padStart(2, '0') + ':' +
String(now.getSeconds()).padStart(2, '0')
return currentTime
}

View File

@@ -10,7 +10,7 @@
import { computed, onMounted } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const url = ''
const url = 'https://www.minio-api.aida.com.hk/fida-test/furniture/sketches/1a48ed3a-1faa-4fcd-bf07-765dba1702c5.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260320%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260320T020948Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=7dc192bac887bce7b02c99d7037c08d9d684310f00add9b0e63b74b36ee63d37'
const openCanvas = () => {
myEvent.emit('openFlowCanvas', { url })
}