进度保存
@ -0,0 +1,6 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon">
|
||||
<rect id="Rectangle 1430107587" x="3.5" y="7.5" width="10" height="2" rx="1" fill="white"/>
|
||||
<rect id="Rectangle 1430107588" x="9.5" y="3.5" width="10" height="2" rx="1" transform="rotate(90 9.5 3.5)" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 337 B |
@ -0,0 +1,7 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M13.9266 6.86919C14.0109 6.30393 13.9832 5.72523 13.8437 5.16879C13.6594 4.43373 13.2798 3.73759 12.705 3.16276C12.1301 2.58793 11.434 2.20836 10.699 2.02404C10.1425 1.88452 9.5638 1.85688 8.99854 1.94114" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_2" d="M11.3965 6.60115C11.4868 5.99562 11.2989 5.35662 10.8327 4.89049C10.3666 4.42435 9.72757 4.23638 9.12207 4.32665" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_3" d="M4.79189 2.93164C5.03409 2.93164 5.25723 3.06298 5.37476 3.27473L6.19026 4.74366C6.29703 4.936 6.30203 5.16863 6.20366 5.3654L5.41806 6.9366C5.41806 6.9366 5.64573 8.1071 6.59853 9.0599C7.55133 10.0127 8.71789 10.2364 8.71789 10.2364L10.2889 9.45096C10.4858 9.3525 10.7186 9.3576 10.911 9.46456L12.3841 10.2836C12.5956 10.4012 12.7268 10.6242 12.7268 10.8662V12.5574C12.7268 13.4186 11.9268 14.0407 11.1108 13.7653C9.43483 13.1998 6.83326 12.1231 5.18433 10.4741C3.53536 8.82516 2.45863 6.2236 1.89311 4.54763C1.61777 3.7316 2.2398 2.93164 3.10102 2.93164H4.79189Z" stroke="#333333" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,14 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame" clip-path="url(#clip0_1445_6177)">
|
||||
<path id="Vector" d="M13.6668 14.667V6.66699H2.3335V14.667H13.6668Z" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_2" d="M8 14.667V6.66699" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_3" d="M13.6668 14.667H2.3335" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_4" d="M14.6668 4H1.3335V6.66667H14.6668V4Z" stroke="#333333" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<path id="Vector_5" d="M5.3335 1.33301L8.00016 3.99967L10.6668 1.33301" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1445_6177">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 953 B |
@ -0,0 +1,19 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="编组 4 1" clip-path="url(#clip0_1445_6121)">
|
||||
<g id="后台(深色)">
|
||||
<g id="首页">
|
||||
<g id="编组-2备份">
|
||||
<g id="编组-4">
|
||||
<path id="Vector 1090" d="M3 3L13 13" stroke="#FF383C" stroke-width="2" stroke-linecap="round"/>
|
||||
<path id="Vector 1091" d="M13 3L3 13" stroke="#FF383C" stroke-width="2" stroke-linecap="round"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1445_6121">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 805 B |
@ -0,0 +1,7 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M2.3335 12.3337C2.3335 9.76589 2.3335 3.66699 2.3335 3.66699C2.3335 2.56242 3.22893 1.66699 4.3335 1.66699H11.6668V10.3337C11.6668 10.3337 6.0777 10.3337 4.3335 10.3337C3.2335 10.3337 2.3335 11.2284 2.3335 12.3337Z" stroke="#333333" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<path id="Vector_2" d="M11.6668 10.333C11.6668 10.333 4.71806 10.333 4.3335 10.333C3.22893 10.333 2.3335 11.2284 2.3335 12.333C2.3335 13.4376 3.22893 14.333 4.3335 14.333C5.06986 14.333 8.62543 14.333 13.6668 14.333V2.33301" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_3" d="M4.6665 12.333H11.3332" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 864 B |
@ -0,0 +1,9 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M11.3335 3.33301L14.0002 5.99967" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_2" d="M14.0002 3.33301L11.3335 5.99967" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_3" d="M14.6667 10L12.3333 12.6667L11 11.3333" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_4" d="M8.66683 3.33301H1.3335V5.99967H8.66683V3.33301Z" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_5" d="M8.66683 10H1.3335V12.6667H8.66683V10Z" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 859 B |
@ -0,0 +1,18 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="编组 4 1" clip-path="url(#clip0_1445_6148)">
|
||||
<g id="后台(深色)">
|
||||
<g id="首页">
|
||||
<g id="编组-2备份">
|
||||
<g id="编组-4">
|
||||
<path id="Vector 1091" d="M15.0002 3L5.99032 12L1 7.13962" stroke="#34C759" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1445_6148">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 753 B |
@ -0,0 +1,16 @@
|
||||
<svg width="71" height="70" viewBox="0 0 71 70" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame" clip-path="url(#clip0_1445_7161)">
|
||||
<path id="Vector" d="M62.5157 63.3503C62.5157 64.2253 62.1603 65.0653 61.5215 65.6951C60.8819 66.3248 60.0308 66.6752 59.1425 66.6752H11.8215C10.934 66.6752 10.0465 66.3248 9.44332 65.6951C8.80457 65.0645 8.44922 64.2253 8.44922 63.3503V3.32483C8.44922 2.44983 8.8037 1.57483 9.44332 0.980103C10.0465 0.350342 10.9349 0 11.8215 0H40.8612C41.7487 0 42.6362 0.350342 43.2749 0.980103L61.5571 19.0056C62.1958 19.6354 62.5512 20.4753 62.5512 21.3854V63.3503H62.5157Z" fill="#EBECF0"/>
|
||||
<path id="Vector_2" d="M62.5156 63.3506V66.6754C62.5156 67.5504 62.1603 68.4254 61.5215 69.0201C60.8819 69.6508 60.0308 70.0002 59.1425 70.0002H11.8215C9.93994 70.0002 8.44922 68.4955 8.44922 66.6754V63.3506C8.44922 64.2256 8.8037 65.0656 9.44332 65.6953C10.0821 66.3251 10.934 66.6754 11.8215 66.6754H59.1433C61.0249 66.6754 62.5156 65.1707 62.5156 63.3506Z" fill="#C1C7D0"/>
|
||||
<path id="Vector_3" d="M8.44913 36.6797V29.9941L1.66895 36.6797H8.44913ZM62.5156 36.6797L62.5866 29.9941L69.3313 36.6797H62.5156Z" fill="#D30000"/>
|
||||
<path id="Vector_4" d="M62.5157 21.3854V21.84H44.2327C42.3511 21.84 40.8604 20.3352 40.8604 18.5151V0C41.7479 0 42.6354 0.350342 43.2741 0.980103L61.5927 19.0056C62.1959 19.6354 62.5512 20.4753 62.5157 21.3854Z" fill="#C1C7D0"/>
|
||||
<path id="Vector_5" d="M34.8708 41.7754H32.0896V51.5499H34.8214C37.9242 51.5499 39.6013 49.8264 39.6013 46.5016C39.6013 43.6083 38.0672 41.8138 34.8708 41.7754ZM19.064 41.7805H15.085V45.9342H19.064C20.6942 45.9342 21.7247 45.3831 21.7247 43.8296C21.7247 42.5632 20.9352 41.7805 19.064 41.7805Z" fill="#FF1D1D"/>
|
||||
<path id="Vector_6" d="M1.7041 36.6797V53.3047C1.7041 54.2147 2.05858 55.0547 2.69821 55.6844C3.30143 56.3151 4.18893 56.6645 5.07643 56.6645H65.9239C67.77 56.6645 69.2962 55.1948 69.2962 53.3397V36.6797H1.7041ZM19.3622 48.8801H15.0851V54.4907H11.2161V38.8535H19.4229C23.1107 38.8535 25.5626 40.6915 25.5626 43.7686C25.5626 46.8456 23.2528 48.8801 19.3622 48.8801ZM35.3286 54.4907H28.2407V38.8535H35.41C40.7541 38.8535 43.6783 42.2732 43.6783 46.3987C43.6783 51.4043 39.9446 54.4907 35.3286 54.4907ZM59.8969 41.8724H50.6934V45.4288H59.2781V48.47H50.6934V54.4899H46.8444V38.8527H59.8969V41.8716V41.8724Z" fill="#FF1D1D"/>
|
||||
<path id="Vector_7" d="M19.4227 38.8535H11.2168V54.4907H15.0857V48.8801H19.3629C23.2535 48.8801 25.5633 46.9661 25.5633 43.7686C25.5633 40.571 23.1114 38.8535 19.4236 38.8535H19.4227ZM19.0639 45.9338H15.0849V41.7802H19.0639C20.9351 41.7802 21.7247 42.5629 21.7247 43.8292C21.7247 45.3818 20.6933 45.9338 19.0639 45.9338ZM35.4107 38.8535H28.2414V54.4907H35.3292C39.9453 54.4907 43.679 51.4043 43.679 46.3987C43.679 42.2732 40.7556 38.8535 35.4107 38.8535ZM34.8214 51.5496H32.0895V41.775H34.8708C38.0663 41.8143 39.6012 43.6088 39.6012 46.5012C39.6012 49.826 37.9241 51.5496 34.8214 51.5496ZM46.8451 54.4907H50.6941V48.4708H59.2787V45.4297H50.6941V41.8733H59.8976V38.8535H46.8451V54.4907Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1445_7161">
|
||||
<rect width="71" height="70" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 374 KiB |
|
After Width: | Height: | Size: 374 KiB |
|
After Width: | Height: | Size: 5.6 MiB |
@ -0,0 +1,10 @@
|
||||
<svg width="70" height="70" viewBox="0 0 70 70" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M61.5978 63.2586C61.5996 63.6955 61.5144 64.1283 61.3472 64.5319C61.1799 64.9355 60.934 65.3017 60.6237 65.6092C59.9998 66.2319 59.1546 66.5821 58.2731 66.5833H11.6944C11.2577 66.585 10.8249 66.4998 10.4214 66.3326C10.0179 66.1653 9.65169 65.9195 9.34419 65.6092C8.72138 64.9854 8.37114 64.1402 8.37014 63.2586V3.37818C8.3684 2.94119 8.45363 2.50821 8.62086 2.10448C8.78808 1.70074 9.03397 1.33431 9.34419 1.02653C9.96802 0.403934 10.8131 0.0538255 11.6944 0.0528325H40.2684C40.7066 0.0494643 41.1412 0.132837 41.547 0.298148C41.9528 0.463459 42.3219 0.707439 42.633 1.01603L60.6353 19.0172C60.9435 19.3286 61.1871 19.6978 61.3523 20.1036C61.5175 20.5093 61.6009 20.9437 61.5978 21.3818V63.2586Z" fill="#EBECF0"/>
|
||||
<path id="Vector_2" d="M61.5984 63.2594V66.5875C61.6001 67.0244 61.5148 67.4574 61.3476 67.861C61.1804 68.2647 60.9345 68.6311 60.6243 68.9388C60.0004 69.5615 59.1552 69.9117 58.2737 69.9129H11.695C10.8124 69.9124 9.96606 69.5615 9.34205 68.9373C8.71803 68.3131 8.3674 67.4666 8.36722 66.584V63.2594C8.36538 63.6962 8.45053 64.129 8.6177 64.5326C8.78487 64.9361 9.03072 65.3024 9.34092 65.61C9.96477 66.2328 10.81 66.583 11.6915 66.584H58.2667C59.1487 66.5817 59.9939 66.2303 60.6175 65.6067C61.2412 64.983 61.5926 64.1378 61.5949 63.2559L61.5984 63.2594Z" fill="#C1C7D0"/>
|
||||
<path id="Vector_3" d="M1.7168 36.6475H68.2472V53.2819C68.2449 54.163 67.8941 55.0074 67.2714 55.6309C66.6487 56.2543 65.8048 56.6062 64.9236 56.6097H5.04565C4.60874 56.6114 4.17584 56.5263 3.77217 56.3591C3.3685 56.1919 3.00212 55.9461 2.69435 55.636C2.07153 55.0122 1.72129 54.1669 1.7203 53.2854L1.7168 36.6475Z" fill="#317BFF"/>
|
||||
<path id="Vector_4" d="M8.37345 36.6462V29.9902L1.7168 36.6462H8.37345ZM61.5976 36.6462L61.6613 29.9902L68.2826 36.6462H61.5976Z" fill="#234AE8"/>
|
||||
<path id="Vector_5" d="M61.5976 21.3789V21.8465H43.5971C42.7144 21.8465 41.8679 21.4959 41.2437 20.8719C40.6195 20.2478 40.2687 19.4014 40.2686 18.5187V0.0499002C40.7067 0.0465782 41.1411 0.129974 41.5469 0.295283C41.9527 0.460592 42.3217 0.704549 42.6328 1.0131L60.6634 19.0073C61.2731 19.6452 61.6084 20.4966 61.5976 21.3789Z" fill="#C1C7D0"/>
|
||||
<path id="Vector_6" d="M17.7514 41.4655L19.1041 48.4757L20.3956 41.4652H23.9621L21.195 52.7803H17.5053L16.2138 46.2007H16.1522L14.8611 52.7803H11.233L8.4043 41.4655H12.0324L13.2623 48.4757H13.3239L14.6766 41.4652L17.7514 41.4655ZM30.6034 41.1582C34.3332 41.3225 36.3215 43.2902 36.5681 47.0613C36.4036 50.8336 34.4154 52.8014 30.6034 52.9648C26.7907 52.8418 24.8229 50.8534 24.6999 46.9997C24.9046 43.3105 26.8724 41.3633 30.6034 41.1582ZM30.6034 43.9253C29.209 43.9666 28.4711 45.0119 28.3896 47.0613C28.4713 49.1935 29.2092 50.2799 30.6034 50.3205C32.0792 50.2799 32.8376 49.1935 32.8784 47.0613C32.7958 44.9706 32.0375 43.9253 30.6034 43.9253ZM42.9633 48.8449H41.8566V52.78H38.1672V41.4652H43.517C47.0007 41.3835 48.6608 42.4494 48.4975 44.6628C48.4567 46.1388 47.8212 47.0613 46.591 47.4302C47.984 47.7172 48.6401 48.7215 48.5591 50.443V50.9351C48.518 52.0418 48.6411 52.5338 48.9283 52.4111V52.78H45.1767C45.0544 52.5338 44.9932 51.8779 44.9929 50.8123C45.1563 49.3777 44.4797 48.7218 42.9633 48.8446V48.8449ZM41.8566 44.1098V46.3235H43.3325C44.5622 46.3648 45.1564 46.0163 45.1151 45.2781C45.0743 44.5403 44.5621 44.1508 43.5786 44.1098H41.8566ZM55.3232 52.78H50.4652V41.4655H55.4464C59.3407 41.5068 61.3086 43.3107 61.3498 46.8772C61.3498 50.8126 59.341 52.7803 55.3232 52.7803V52.78ZM54.1552 44.1098V50.136H54.9543C56.7164 50.136 57.5977 49.1111 57.5982 47.0613C57.7214 44.9303 56.8196 43.9464 54.8927 44.1098H54.1552Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M14.2511 12.9624H9.27535L14.3045 7.98956C14.7499 7.54847 15 6.95078 15 6.32764C15 5.70449 14.7499 5.1068 14.3045 4.66571L10.2798 0.688151C9.83353 0.247515 9.22853 0 8.59771 0C7.9669 0 7.36189 0.247515 6.91565 0.688151L0.696596 6.83261C0.475749 7.05085 0.300563 7.30995 0.18104 7.5951C0.0615177 7.88026 0 8.18589 0 8.49454C0 8.80319 0.0615177 9.10882 0.18104 9.39398C0.300563 9.67913 0.475749 9.93823 0.696596 10.1565L3.53577 12.9624H0.567981C0.428752 12.9624 0.295226 13.017 0.196776 13.1143C0.0983268 13.2116 0.0430185 13.3436 0.0430185 13.4812C0.0430185 13.6188 0.0983268 13.7507 0.196776 13.848C0.295226 13.9453 0.428752 14 0.567981 14H14.2511C14.3903 14 14.5239 13.9453 14.6223 13.848C14.7208 13.7507 14.7761 13.6188 14.7761 13.4812C14.7761 13.3436 14.7208 13.2116 14.6223 13.1143C14.5239 13.017 14.3903 12.9624 14.2511 12.9624ZM1.44292 7.56587L7.65934 1.41881C7.78248 1.29679 7.92882 1.20005 8.08994 1.13416C8.25106 1.06827 8.42378 1.03453 8.59815 1.03489H8.6524L7.17463 6.91044C7.16687 6.94256 7.15051 6.97203 7.12727 6.99577C7.10402 7.0195 7.07475 7.03662 7.04251 7.04533L1.05532 8.51529V8.49281C1.05482 8.32067 1.08881 8.15013 1.15533 7.99104C1.22186 7.83194 1.3196 7.68744 1.44292 7.56587ZM1.49804 9.47769L7.2945 8.05269C7.51278 7.99943 7.71219 7.88824 7.87117 7.73112C8.03016 7.574 8.14267 7.37692 8.19656 7.1612L9.62183 1.49836L13.5687 5.39896C13.8174 5.64535 13.957 5.97918 13.957 6.3272C13.957 6.67523 13.8174 7.00905 13.5687 7.25545L7.79408 12.9624H5.02053L1.49804 9.47769Z" fill="#333333"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,7 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M5.1249 3.26562C5.3671 3.26562 5.59023 3.39697 5.70777 3.6087L6.52327 5.07767C6.63003 5.27 6.63503 5.5026 6.53667 5.69937L5.75107 7.2706C5.75107 7.2706 5.97873 8.44107 6.93153 9.39387C7.88433 10.3467 9.0509 10.5704 9.0509 10.5704L10.6219 9.78494C10.8188 9.6865 11.0516 9.6916 11.244 9.79857L12.7171 10.6176C12.9286 10.7352 13.0598 10.9582 13.0598 11.2002V12.8914C13.0598 13.7526 12.2598 14.3746 11.4438 14.0993C9.76783 13.5338 7.16627 12.457 5.51733 10.8081C3.86837 9.15914 2.79163 6.5576 2.22612 4.8816C1.95078 4.0656 2.57281 3.26562 3.43403 3.26562H5.1249Z" stroke="#1D2129" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<path id="Vector_2" d="M9.66699 6.66665L14.0003 2.5" stroke="#1D2129" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_3" d="M14.0003 6.66535H9.66699V2.33203" stroke="#1D2129" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1,9 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M11.333 3.33203L13.9997 5.9987" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_2" d="M13.9997 3.33203L11.333 5.9987" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_3" d="M14.6667 10L12.3333 12.6667L11 11.3333" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_4" d="M8.66634 3.33203H1.33301V5.9987H8.66634V3.33203Z" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Vector_5" d="M8.66634 10H1.33301V12.6667H8.66634V10Z" stroke="#333333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 856 B |
@ -0,0 +1,18 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 1707486099">
|
||||
<rect id="Rectangle 1430107575" width="48" height="48" rx="24" fill="url(#paint0_linear_1445_6607)"/>
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M26.7397 23.4055L24.3361 25.8091L26.3534 33.205L24.0002 35.8947L21.6471 33.205L23.6644 25.8091L21.2608 23.4055C15.4922 24.6245 11.5615 29.4829 11.5615 32.8692C11.5615 36.7676 20.1011 38.5837 24.0002 38.5837C27.8994 38.5837 36.439 36.7676 36.439 32.8692C36.439 29.4829 32.5083 24.6245 26.7397 23.4055ZM24.0002 23.1201C24.9274 23.1201 25.8454 22.9375 26.702 22.5827C27.5586 22.2279 28.3369 21.7079 28.9925 21.0523C29.6481 20.3967 30.1681 19.6184 30.5229 18.7618C30.8777 17.9053 31.0603 16.9872 31.0603 16.0601C31.0603 15.1329 30.8777 14.2149 30.5229 13.3583C30.1681 12.5017 29.6481 11.7234 28.9925 11.0678C28.3369 10.4123 27.5586 9.89222 26.702 9.53742C25.8454 9.18261 24.9274 9 24.0002 9C22.1403 9.01894 20.3631 9.77106 19.0545 11.093C17.746 12.4149 17.012 14.1997 17.012 16.0597C17.012 17.9197 17.746 19.7046 19.0545 21.0265C20.3631 22.3483 22.1403 23.1005 24.0002 23.1194V23.1201Z" fill="url(#paint1_linear_1445_6607)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1445_6607" x1="24" y1="0" x2="24" y2="48" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#6155F5"/>
|
||||
<stop offset="1" stop-color="#CDC9FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1445_6607" x1="24.0002" y1="9" x2="24.0002" y2="38.5837" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Union" d="M12.5 0C13.327 0 14 0.673 14 1.5V13.5C14 14.327 13.327 15 12.5 15H2.5C1.673 15 1 14.327 1 13.5V1.5C1 0.673 1.673 0 2.5 0H12.5ZM2.5 1C2.2245 1 2 1.2245 2 1.5V13.5C2 13.776 2.2245 14 2.5 14H12.5C12.6326 14 12.7597 13.9473 12.8535 13.8535C12.9473 13.7597 13 13.6326 13 13.5V1.5C13 1.2245 12.776 1 12.5 1H11.5V2.5C11.5 3.0515 11.0515 3.5 10.5 3.5H4.5C3.9485 3.5 3.5 3.0515 3.5 2.5V1H2.5ZM11.0889 5.08887C11.1545 5.08887 11.2196 5.10184 11.2803 5.12695C11.3409 5.15208 11.396 5.18893 11.4424 5.23535C11.4888 5.28177 11.5257 5.33681 11.5508 5.39746C11.5759 5.4581 11.5889 5.52324 11.5889 5.58887C11.5888 5.65444 11.5759 5.71969 11.5508 5.78027C11.5257 5.84079 11.4887 5.89605 11.4424 5.94238L7.19238 10.1924C7.09864 10.286 6.97134 10.3389 6.83887 10.3389C6.70637 10.3388 6.57907 10.2861 6.48535 10.1924L4.25293 7.95898C4.15927 7.86524 4.10645 7.73799 4.10645 7.60547C4.10649 7.47294 4.15921 7.34567 4.25293 7.25195C4.34665 7.15824 4.47391 7.1055 4.60645 7.10547C4.73897 7.10547 4.86622 7.15829 4.95996 7.25195L6.83887 9.13184L10.7354 5.23535C10.7817 5.18901 10.8369 5.15207 10.8975 5.12695C10.9581 5.10186 11.0233 5.08889 11.0889 5.08887ZM4.5 1V2.5H10.5V1H4.5Z" fill="#1D2129"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,18 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 1707486100">
|
||||
<rect id="Rectangle 1430107575" width="48" height="48" rx="24" fill="url(#paint0_linear_1445_6615)"/>
|
||||
<g id="Frame">
|
||||
<path id="Union" d="M15.8926 12.4814V16.1104C15.8926 18.0246 17.4246 19.556 19.3389 19.5713H28.3281C30.2424 19.5713 31.7734 18.0402 31.7734 16.126V12.4814H32.3555C34.2697 12.4814 35.8017 14.0125 35.8018 15.9268V26.6777C34.6685 25.9273 33.3205 25.3447 31.7891 25.3447C31.4062 25.3447 30.8397 25.3455 30.2578 25.5293H17.2402C16.6738 25.5295 16.291 25.912 16.291 26.4785C16.2911 27.0449 16.6586 27.4276 17.2402 27.4277H26.9961C26.2305 28.1934 25.6631 29.3418 25.2803 30.4902H17.2402C16.6738 30.4904 16.291 30.8586 16.291 31.4404C16.2911 32.0068 16.6586 32.3895 17.2402 32.3896H25.0811C25.2648 35.4525 27.3789 37.949 30.0742 38.5156H14.9434C13.0292 38.5155 11.498 36.9836 11.498 35.0693V15.9268C11.4981 14.0126 13.0292 12.4815 14.9434 12.4814H15.8926ZM31.9727 26.8457C33.5041 26.8457 35.0359 27.6112 35.9854 28.7598C36.5673 29.7092 37.1496 30.8583 36.9658 32.0068C36.9658 33.339 36.5821 34.5027 35.8164 35.4521C34.867 36.6006 33.336 37.3661 31.8047 37.3662C29.1095 37.3662 26.8118 35.2686 26.6279 32.3896V32.2061C26.6279 31.6394 26.6287 31.0722 26.8125 30.4902C27.3791 29.1427 28.3437 28.0096 29.4922 27.4277C30.0741 27.0449 30.4415 26.8457 31.0234 26.8457H31.9727ZM34.8369 30.0918C34.4542 29.7092 34.0712 29.7094 33.6885 30.0918L31.207 32.5732L29.875 31.2412C29.4921 30.8584 29.1094 30.8584 28.7266 31.2412C28.3438 31.624 28.3437 32.0068 28.7266 32.3896L30.6406 34.3037C31.0234 34.6865 31.4063 34.6865 31.7891 34.3037L34.8516 31.2412C35.2191 30.8584 35.2198 30.4747 34.8369 30.0918ZM23.7334 10C25.2647 10 26.4293 11.1483 26.6133 12.4805H30.0586V16.126C30.0584 17.0752 29.293 17.8407 28.3438 17.8408H19.3389C18.3742 17.8408 17.6086 17.0753 17.6084 16.126V12.4805H21.0537C21.2376 11.1485 22.3861 10.0003 23.7334 10Z" fill="url(#paint1_linear_1445_6615)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1445_6615" x1="24" y1="0" x2="24" y2="48" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#2DB74F"/>
|
||||
<stop offset="1" stop-color="#ACE5BA"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1445_6615" x1="24.2492" y1="10" x2="24.2492" y2="38.5156" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@ -0,0 +1,18 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 1707486101">
|
||||
<path id="Rectangle 1430107575" d="M0 24C0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24Z" fill="url(#paint0_linear_1445_6625)"/>
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M10 24C10 27.713 11.475 31.274 14.1005 33.8995C16.726 36.525 20.287 38 24 38C27.713 38 31.274 36.525 33.8995 33.8995C36.525 31.274 38 27.713 38 24C38 20.287 36.525 16.726 33.8995 14.1005C31.274 11.475 27.713 10 24 10C20.287 10 16.726 11.475 14.1005 14.1005C11.475 16.726 10 20.287 10 24ZM31.2471 20.4722L22.4568 28.8483C22.2189 29.0747 21.9041 29.2025 21.5758 29.2061C21.2474 29.2098 20.9299 29.0889 20.687 28.8679L17.2686 25.762C17.0134 25.5304 16.8607 25.2069 16.8441 24.8628C16.8358 24.6923 16.8612 24.522 16.9188 24.3614C16.9764 24.2008 17.065 24.0531 17.1797 23.9267C17.2944 23.8004 17.4328 23.6979 17.5871 23.6251C17.7414 23.5522 17.9085 23.5105 18.0789 23.5022C18.4231 23.4856 18.7598 23.6063 19.0149 23.8379L21.5387 26.1331L29.4578 18.593C29.5796 18.4677 29.7254 18.3682 29.8866 18.3006C30.0477 18.233 30.2208 18.1986 30.3956 18.1994C30.5704 18.2003 30.7431 18.2364 30.9036 18.3056C31.0641 18.3748 31.2089 18.4757 31.3295 18.6022C31.4501 18.7286 31.5439 18.8782 31.6053 19.0418C31.6668 19.2054 31.6946 19.3797 31.6871 19.5543C31.6795 19.7289 31.6369 19.9001 31.5616 20.0579C31.4864 20.2156 31.3801 20.3565 31.2491 20.4722H31.2471Z" fill="url(#paint1_linear_1445_6625)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1445_6625" x1="24" y1="0" x2="24" y2="48" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00C3D0"/>
|
||||
<stop offset="1" stop-color="#ACF6FB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1445_6625" x1="24" y1="10" x2="24" y2="38" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,18 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 1707486098">
|
||||
<rect id="Rectangle 1430107575" width="48" height="48" rx="24" fill="url(#paint0_linear_1445_6596)"/>
|
||||
<g id="Frame">
|
||||
<path id="Union" d="M34.9297 28.6836C35.5059 29.3815 35.8242 30.1492 35.8242 30.9551C35.8242 34.1692 30.7543 36.7744 24.5 36.7744C18.2456 36.7744 13.1758 34.1692 13.1758 30.9551C13.1758 30.1492 13.4941 29.3815 14.0703 28.6836C15.7924 30.769 19.8139 32.2314 24.5 32.2314C29.186 32.2314 33.2076 30.769 34.9297 28.6836ZM34.8613 22.1494C35.4803 22.8685 35.8242 23.6641 35.8242 24.501C35.8242 27.7151 30.7543 30.3213 24.5 30.3213C18.2456 30.3213 13.1758 27.7151 13.1758 24.501C13.1758 23.6641 13.5197 22.8685 14.1387 22.1494C15.8968 24.1919 19.8744 25.6172 24.5 25.6172C29.1256 25.6172 33.1032 24.192 34.8613 22.1494ZM24.5 12.2266C27.5034 12.2266 30.3841 12.8393 32.5078 13.9307C34.6314 15.022 35.824 16.5025 35.8242 18.0459C35.8242 18.8101 35.5319 19.5674 34.9629 20.2734C34.3938 20.9795 33.5593 21.6208 32.5078 22.1611C31.4563 22.7015 30.2078 23.1304 28.834 23.4229C27.46 23.7153 25.9871 23.8662 24.5 23.8662C23.0128 23.8662 21.54 23.7153 20.166 23.4229C18.7922 23.1304 17.5437 22.7015 16.4922 22.1611C15.4407 21.6207 14.6062 20.9795 14.0371 20.2734C13.4681 19.5674 13.1758 18.8101 13.1758 18.0459C13.176 16.5025 14.3686 15.022 16.4922 13.9307C18.6159 12.8393 21.4966 12.2266 24.5 12.2266Z" fill="url(#paint1_linear_1445_6596)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1445_6596" x1="24" y1="0" x2="24" y2="48" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3289FB"/>
|
||||
<stop offset="1" stop-color="#74B0FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1445_6596" x1="24.5" y1="12.2266" x2="24.5" y2="36.7744" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M11.9705 13.8493L13.7604 7.51594C13.8806 7.09047 13.5609 6.66797 13.1188 6.66797H9.29157C9.05713 6.66797 8.88367 6.4499 8.93637 6.22147L9.10287 5.5C9.2556 4.8382 9.30463 4.15674 9.2482 3.4799L9.21757 3.1122C9.1847 2.71775 9.0131 2.34772 8.7332 2.06784C8.47717 1.81181 8.12993 1.66797 7.76783 1.66797H7.68813C7.46913 1.66797 7.26737 1.78669 7.16103 1.97811L6.275 3.57294C5.87763 4.28824 5.2853 4.87597 4.56697 5.2678L2.34743 6.47847C2.13326 6.59527 2 6.81977 2 7.06374V13.668C2 14.0362 2.29848 14.3346 2.66667 14.3346H11.329C11.6273 14.3346 11.8894 14.1364 11.9705 13.8493Z" stroke="#1D2129" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 803 B |
@ -0,0 +1,250 @@
|
||||
/**
|
||||
* 报告 DOCX 导出 composable
|
||||
*
|
||||
* 与 useReportPdfExport 类似,但输出 .docx 文件:
|
||||
* - 用 html2canvas 把 .body-wrap 截图成 canvas
|
||||
* - canvas 转 PNG bytes
|
||||
* - 用 docx 库把 PNG 嵌入 A4 大小的 docx 中
|
||||
*
|
||||
* 用法:
|
||||
* const { exportReportDocx } = useReportDocxExport()
|
||||
* await exportReportDocx(bodyWrapEl, 'report.docx')
|
||||
*/
|
||||
|
||||
import html2canvas from 'html2canvas'
|
||||
import {
|
||||
AlignmentType,
|
||||
Document,
|
||||
HeightRule,
|
||||
ImageRun,
|
||||
Packer,
|
||||
PageOrientation,
|
||||
Paragraph,
|
||||
} from 'docx'
|
||||
import { saveAs } from 'file-saver'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
// A4 尺寸(单位 twip,1 inch = 1440 twip, 1mm ≈ 56.6929 twip)
|
||||
const A4_WIDTH_TWIP = 11906
|
||||
const A4_HEIGHT_TWIP = 16838
|
||||
const MARGIN_TWIP = 850 // 约 15mm
|
||||
|
||||
export interface ExportReportDocxOptions {
|
||||
/** 离屏容器宽度,默认 800 */
|
||||
width?: number
|
||||
/** 缩放比例,值越大越清晰,默认 2 */
|
||||
scale?: number
|
||||
/** 临时隐藏元素的选择器列表 */
|
||||
hideSelectors?: string[]
|
||||
/** 是否不显示内部 loading 提示 */
|
||||
silent?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* canvas → PNG ArrayBuffer (docx 库 ImageRun 需要 Uint8Array)
|
||||
*/
|
||||
function canvasToPngBuffer(canvas: HTMLCanvasElement): Promise<Uint8Array> {
|
||||
return new Promise((resolve, reject) => {
|
||||
canvas.toBlob(async (blob) => {
|
||||
if (!blob) {
|
||||
reject(new Error('canvas 转 Blob 失败'))
|
||||
return
|
||||
}
|
||||
try {
|
||||
const buf = await blob.arrayBuffer()
|
||||
resolve(new Uint8Array(buf))
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
}, 'image/png')
|
||||
})
|
||||
}
|
||||
|
||||
export function useReportDocxExport() {
|
||||
/**
|
||||
* 将原 DOM 节点导出为 DOCX。
|
||||
*/
|
||||
const exportReportDocx = async (
|
||||
sourceEl: HTMLElement,
|
||||
fileName: string,
|
||||
options: ExportReportDocxOptions = {},
|
||||
): Promise<void> => {
|
||||
if (!sourceEl) {
|
||||
ElMessage.error('未找到可导出的报告内容')
|
||||
return
|
||||
}
|
||||
|
||||
const {
|
||||
width = 800,
|
||||
scale = 2,
|
||||
hideSelectors = ['.body-header', '.el-loading-mask', '.el-loading-spinner'],
|
||||
silent = false,
|
||||
} = options
|
||||
|
||||
// 1) 记录原父节点 + 下一个兄弟节点,截图完毕后还原
|
||||
const originalParent = sourceEl.parentNode as HTMLElement
|
||||
const originalNext = sourceEl.nextSibling as ChildNode | null
|
||||
|
||||
// 2) 读取 sourceEl 的计算样式,移动后强制 inline 注入 padding
|
||||
const originalStyleAttr = sourceEl.getAttribute('style') || ''
|
||||
const computed = window.getComputedStyle(sourceEl)
|
||||
sourceEl.style.paddingTop = computed.paddingTop
|
||||
sourceEl.style.paddingRight = computed.paddingRight
|
||||
sourceEl.style.paddingBottom = computed.paddingBottom
|
||||
sourceEl.style.paddingLeft = computed.paddingLeft
|
||||
|
||||
// 3) 临时隐藏指定元素
|
||||
const hiddenDisplays: Array<{ el: HTMLElement; prev: string }> = []
|
||||
hideSelectors.forEach((sel) => {
|
||||
sourceEl.querySelectorAll<HTMLElement>(sel).forEach((el) => {
|
||||
hiddenDisplays.push({ el, prev: el.style.display })
|
||||
el.style.display = 'none'
|
||||
})
|
||||
})
|
||||
|
||||
// 4) 创建离屏容器
|
||||
const offScreen = document.createElement('div')
|
||||
offScreen.style.cssText = [
|
||||
'position: fixed',
|
||||
'left: -99999px',
|
||||
'top: 0',
|
||||
`width: ${width}px`,
|
||||
'background: #ffffff',
|
||||
'z-index: -1',
|
||||
'pointer-events: none',
|
||||
].join(';')
|
||||
document.body.appendChild(offScreen)
|
||||
offScreen.appendChild(sourceEl)
|
||||
|
||||
// 4.1) 强制 sourceEl 宽度,让 ECharts 按目标宽度重绘 canvas
|
||||
const prevWidth = sourceEl.style.width
|
||||
const prevMaxWidth = sourceEl.style.maxWidth
|
||||
const prevBoxSizing = sourceEl.style.boxSizing
|
||||
sourceEl.style.width = `${width}px`
|
||||
sourceEl.style.maxWidth = `${width}px`
|
||||
sourceEl.style.boxSizing = 'border-box'
|
||||
|
||||
let loadingMsg: { close: () => void } | null = null
|
||||
if (!silent) {
|
||||
loadingMsg = ElMessage({
|
||||
message: '正在生成 Word…',
|
||||
type: 'info',
|
||||
duration: 0,
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
// 给浏览器一帧时间让样式/layout 生效
|
||||
await new Promise((r) => requestAnimationFrame(() => r(null)))
|
||||
await new Promise((r) => requestAnimationFrame(() => r(null)))
|
||||
// 等 ECharts canvas 完成重绘
|
||||
await new Promise((r) => setTimeout(r, 1500))
|
||||
|
||||
const canvas = await html2canvas(sourceEl, {
|
||||
scale,
|
||||
backgroundColor: '#ffffff',
|
||||
logging: false,
|
||||
useCORS: false,
|
||||
allowTaint: true,
|
||||
windowWidth: width,
|
||||
foreignObjectRendering: false,
|
||||
})
|
||||
|
||||
// canvas → PNG bytes
|
||||
const pngBytes = await canvasToPngBuffer(canvas)
|
||||
|
||||
// docx 库 IMediaTransformation.width/height 单位是 **像素(px)**
|
||||
// A4 可用宽度: 210mm - 2*15mm(边距) = 180mm
|
||||
// 按 96 DPI: 180mm = 7.09 inch = 680 px
|
||||
const usableWidthPx = 680
|
||||
const ratio = canvas.height / canvas.width
|
||||
const imgHeightPx = Math.round(usableWidthPx * ratio)
|
||||
|
||||
// 构造 docx 文档
|
||||
const doc = new Document({
|
||||
creator: 'CSM Report',
|
||||
title: fileName.replace(/\.docx$/i, ''),
|
||||
styles: {
|
||||
default: {
|
||||
document: {
|
||||
run: {
|
||||
font: 'Microsoft YaHei', // 中文字体
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sections: [
|
||||
{
|
||||
properties: {
|
||||
page: {
|
||||
size: {
|
||||
orientation: PageOrientation.PORTRAIT,
|
||||
width: A4_WIDTH_TWIP,
|
||||
height: A4_HEIGHT_TWIP,
|
||||
},
|
||||
margin: {
|
||||
top: MARGIN_TWIP,
|
||||
right: MARGIN_TWIP,
|
||||
bottom: MARGIN_TWIP,
|
||||
left: MARGIN_TWIP,
|
||||
},
|
||||
},
|
||||
},
|
||||
children: [
|
||||
new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
spacing: {
|
||||
after: 0,
|
||||
line: 240,
|
||||
lineRule: HeightRule.AUTO,
|
||||
},
|
||||
children: [
|
||||
new ImageRun({
|
||||
type: 'png', // ← 必填,指定图片类型,否则 Word/WPS 解析失败
|
||||
data: pngBytes,
|
||||
transformation: {
|
||||
width: usableWidthPx, // ← 像素
|
||||
height: imgHeightPx, // ← 像素
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
// 生成 .docx blob 并下载
|
||||
const blob = await Packer.toBlob(doc)
|
||||
saveAs(blob, fileName || 'report.docx')
|
||||
|
||||
if (!silent) ElMessage.success('Word 下载成功')
|
||||
} catch (err) {
|
||||
console.error('[useReportDocxExport] Word 生成失败:', err)
|
||||
if (!silent) ElMessage.error((err as Error)?.message || 'Word 生成失败')
|
||||
throw err
|
||||
} finally {
|
||||
loadingMsg?.close()
|
||||
// 恢复隐藏元素的 display
|
||||
hiddenDisplays.forEach(({ el, prev }) => {
|
||||
el.style.display = prev
|
||||
})
|
||||
// 把原 DOM 移回原位置
|
||||
if (offScreen.parentNode) {
|
||||
offScreen.parentNode.removeChild(offScreen)
|
||||
}
|
||||
if (originalNext && originalNext.parentNode === originalParent) {
|
||||
originalParent.insertBefore(sourceEl, originalNext)
|
||||
} else {
|
||||
originalParent.appendChild(sourceEl)
|
||||
}
|
||||
// 恢复 sourceEl 原 inline style
|
||||
sourceEl.setAttribute('style', originalStyleAttr)
|
||||
sourceEl.style.width = prevWidth
|
||||
sourceEl.style.maxWidth = prevMaxWidth
|
||||
sourceEl.style.boxSizing = prevBoxSizing
|
||||
}
|
||||
}
|
||||
|
||||
return { exportReportDocx }
|
||||
}
|
||||
@ -0,0 +1,194 @@
|
||||
/**
|
||||
* 报告 PDF 导出 composable
|
||||
*
|
||||
* 将一个已渲染好的 DOM 节点截图为整张长图,
|
||||
* 然后由 jsPDF 按 A4 高度自动分页,生成多页 PDF。
|
||||
*
|
||||
* 关键点:
|
||||
* - 不克隆 DOM(避免 canvas/svg 截图空白、scoped 样式丢失)
|
||||
* - 把原 DOM 临时移到离屏容器,截图后恢复原位
|
||||
* - 自动隐藏指定选择器的元素(如 .body-header / .el-loading-mask)
|
||||
*
|
||||
* 用法:
|
||||
* const { exportReportPdf } = useReportPdfExport()
|
||||
* await exportReportPdf(bodyWrapEl, 'report.pdf')
|
||||
*/
|
||||
|
||||
import html2canvas from 'html2canvas'
|
||||
import jsPDF from 'jspdf'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
export interface ExportReportOptions {
|
||||
/** 离屏容器宽度,默认 800 */
|
||||
width?: number
|
||||
/** 缩放比例,值越大越清晰,默认 2 */
|
||||
scale?: number
|
||||
/** JPEG 质量 0~1,默认 0.95 */
|
||||
quality?: number
|
||||
/** 临时隐藏元素的选择器列表(这些元素在截图前 display:none,完成后恢复) */
|
||||
hideSelectors?: string[]
|
||||
/** 是否不显示内部 loading 提示(由调用方自己控制) */
|
||||
silent?: boolean
|
||||
}
|
||||
|
||||
export function useReportPdfExport() {
|
||||
/**
|
||||
* 将原 DOM 节点导出为 PDF。
|
||||
* @param sourceEl 已渲染好的 DOM 节点(通常是 .body-wrap)
|
||||
* @param fileName 下载文件名,需带 .pdf 后缀
|
||||
* @param options 额外配置
|
||||
*/
|
||||
const exportReportPdf = async (
|
||||
sourceEl: HTMLElement,
|
||||
fileName: string,
|
||||
options: ExportReportOptions = {},
|
||||
): Promise<void> => {
|
||||
if (!sourceEl) {
|
||||
ElMessage.error('未找到可导出的报告内容')
|
||||
return
|
||||
}
|
||||
|
||||
const {
|
||||
width = 800,
|
||||
scale = 2,
|
||||
quality = 0.95,
|
||||
hideSelectors = ['.body-header', '.el-loading-mask', '.el-loading-spinner'],
|
||||
silent = false,
|
||||
} = options
|
||||
|
||||
// 1) 记录原父节点 + 下一个兄弟节点,截图完毕后还原
|
||||
const originalParent = sourceEl.parentNode as HTMLElement
|
||||
const originalNext = sourceEl.nextSibling as ChildNode | null
|
||||
|
||||
// 2) 读取 sourceEl 的计算样式,移动后强制 inline 注入 padding(防止全局样式在离屏环境下失效)
|
||||
const originalStyleAttr = sourceEl.getAttribute('style') || ''
|
||||
const computed = window.getComputedStyle(sourceEl)
|
||||
sourceEl.style.paddingTop = computed.paddingTop
|
||||
sourceEl.style.paddingRight = computed.paddingRight
|
||||
sourceEl.style.paddingBottom = computed.paddingBottom
|
||||
sourceEl.style.paddingLeft = computed.paddingLeft
|
||||
|
||||
// 3) 临时隐藏指定元素(完成后恢复原 display 值)
|
||||
const hiddenDisplays: Array<{ el: HTMLElement; prev: string }> = []
|
||||
hideSelectors.forEach((sel) => {
|
||||
sourceEl.querySelectorAll<HTMLElement>(sel).forEach((el) => {
|
||||
hiddenDisplays.push({ el, prev: el.style.display })
|
||||
el.style.display = 'none'
|
||||
})
|
||||
})
|
||||
|
||||
// 4) 创建离屏容器,把原 DOM 移进去(不克隆,保留 canvas/svg/scoped 样式)
|
||||
const offScreen = document.createElement('div')
|
||||
offScreen.style.cssText = [
|
||||
'position: fixed',
|
||||
'left: -99999px',
|
||||
'top: 0',
|
||||
`width: ${width}px`,
|
||||
'background: #ffffff',
|
||||
'z-index: -1',
|
||||
'pointer-events: none',
|
||||
].join(';')
|
||||
document.body.appendChild(offScreen)
|
||||
offScreen.appendChild(sourceEl)
|
||||
|
||||
// 4.1) 强制 sourceEl 宽度,让 ECharts/vue-echarts 按目标宽度重绘 canvas
|
||||
const prevWidth = sourceEl.style.width
|
||||
const prevMaxWidth = sourceEl.style.maxWidth
|
||||
const prevBoxSizing = sourceEl.style.boxSizing
|
||||
sourceEl.style.width = `${width}px`
|
||||
sourceEl.style.maxWidth = `${width}px`
|
||||
sourceEl.style.boxSizing = 'border-box'
|
||||
|
||||
let loadingMsg: { close: () => void } | null = null
|
||||
if (!silent) {
|
||||
loadingMsg = ElMessage({
|
||||
message: '正在生成 PDF…',
|
||||
type: 'info',
|
||||
duration: 0,
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
// 给浏览器一帧时间让样式/layout 生效
|
||||
await new Promise((r) => requestAnimationFrame(() => r(null)))
|
||||
await new Promise((r) => requestAnimationFrame(() => r(null)))
|
||||
|
||||
// 给 ECharts canvas 充分时间完成 resize + 重绘(异步绘制,800ms 不够,延长到 1500ms)
|
||||
await new Promise((r) => setTimeout(r, 1500))
|
||||
|
||||
const canvas = await html2canvas(sourceEl, {
|
||||
scale,
|
||||
backgroundColor: '#ffffff',
|
||||
logging: true,
|
||||
useCORS: false, // 全部本地资源
|
||||
allowTaint: true,
|
||||
windowWidth: width,
|
||||
foreignObjectRendering: false,
|
||||
// onclone 兜底:html2canvas 克隆出的 DOM 里,保护 canvas 不被丢弃 + 表格样式正常
|
||||
onclone: (clonedDoc) => {
|
||||
clonedDoc.querySelectorAll('canvas').forEach((c) => {
|
||||
;(c as HTMLElement).style.display = 'block'
|
||||
;(c as HTMLElement).style.visibility = 'visible'
|
||||
})
|
||||
clonedDoc.querySelectorAll('.report-table').forEach((t) => {
|
||||
;(t as HTMLElement).style.tableLayout = 'auto'
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const imgData = canvas.toDataURL('image/jpeg', quality)
|
||||
|
||||
const pdf = new jsPDF('p', 'mm', 'a4')
|
||||
const pdfWidth = pdf.internal.pageSize.getWidth() // 210mm
|
||||
const pdfHeight = pdf.internal.pageSize.getHeight() // 297mm
|
||||
|
||||
const imgWidth = pdfWidth
|
||||
const imgHeight = (canvas.height * imgWidth) / canvas.width
|
||||
|
||||
let heightLeft = imgHeight
|
||||
let position = 0
|
||||
|
||||
// 第一页(从顶部开始)
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight)
|
||||
heightLeft -= pdfHeight
|
||||
|
||||
// 超出 A4 一页时,继续分页
|
||||
while (heightLeft > 0) {
|
||||
position = heightLeft - imgHeight
|
||||
pdf.addPage()
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight)
|
||||
heightLeft -= pdfHeight
|
||||
}
|
||||
|
||||
pdf.save(fileName)
|
||||
|
||||
if (!silent) ElMessage.success('PDF 下载成功')
|
||||
} catch (err) {
|
||||
console.error('[useReportPdfExport] PDF 生成失败:', err)
|
||||
if (!silent) ElMessage.error((err as Error)?.message || 'PDF 生成失败')
|
||||
throw err
|
||||
} finally {
|
||||
loadingMsg?.close()
|
||||
// 恢复隐藏元素的 display
|
||||
hiddenDisplays.forEach(({ el, prev }) => {
|
||||
el.style.display = prev
|
||||
})
|
||||
// 把原 DOM 移回原位置
|
||||
if (offScreen.parentNode) {
|
||||
offScreen.parentNode.removeChild(offScreen)
|
||||
}
|
||||
if (originalNext && originalNext.parentNode === originalParent) {
|
||||
originalParent.insertBefore(sourceEl, originalNext)
|
||||
} else {
|
||||
originalParent.appendChild(sourceEl)
|
||||
}
|
||||
// 恢复 sourceEl 原 inline style(去掉临时注入的 padding 和宽度)
|
||||
sourceEl.setAttribute('style', originalStyleAttr)
|
||||
sourceEl.style.width = prevWidth
|
||||
sourceEl.style.maxWidth = prevMaxWidth
|
||||
sourceEl.style.boxSizing = prevBoxSizing
|
||||
}
|
||||
}
|
||||
|
||||
return { exportReportPdf }
|
||||
}
|
||||
@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<el-dialog :model-value="modelValue" class="maintenance-detail-dialog" top="6vh" :close-on-click-modal="false"
|
||||
destroy-on-close @update:model-value="(v) => $emit('update:modelValue', v)" @open="onOpen">
|
||||
<div class="body-wrap" v-loading="loading">
|
||||
<div class="body-header">
|
||||
<span class="body-title">维保服务详情 - {{ detail.hospitalName || "—" }}</span>
|
||||
<img src="@/assets/image/close-btn.svg" alt="" class="close-btn" @click="closeDialog" />
|
||||
</div>
|
||||
|
||||
<el-form v-if="detail.hospitalId" ref="formRef" :model="form" :rules="rules" label-width="100px"
|
||||
label-position="right">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户分类">
|
||||
<el-tag :type="categoryTagType(detail.customerCategory)" size="small">
|
||||
{{ detail.customerCategory || "—" }}类
|
||||
</el-tag>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="维保截止日期">
|
||||
<span class="readonly-val">{{ formatDate(detail.maintenanceEnd) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户经理">
|
||||
<span class="readonly-val">{{ detail.managerName || "—" }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider class="thin-divider" />
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="续保完成" prop="renewal">
|
||||
<el-select v-model="form.renewal" placeholder="请选择" clearable style="width: 100%">
|
||||
<el-option label="未完成" value="no" />
|
||||
<el-option label="已完成" value="yes" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合同签署" prop="contractSigned">
|
||||
<el-select v-model="form.contractSigned" placeholder="请选择" clearable style="width: 100%">
|
||||
<el-option label="未签署" value="no" />
|
||||
<el-option label="已签署" value="yes" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="款项支付" prop="paymentPaid">
|
||||
<el-select v-model="form.paymentPaid" placeholder="请选择" clearable style="width: 100%">
|
||||
<el-option label="未支付" value="no" />
|
||||
<el-option label="已支付" value="yes" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="回访状态" prop="visited">
|
||||
<el-select v-model="form.visited" placeholder="请选择" clearable style="width: 100%">
|
||||
<el-option label="未回访" value="no" />
|
||||
<el-option label="已回访" value="yes" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="回访结果" prop="visitResult">
|
||||
<el-input v-model="form.visitResult" placeholder="请输入回访结果" maxlength="500" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" maxlength="1000"
|
||||
show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="body-footer">
|
||||
<el-button @click="closeDialog">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="onSubmit">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import dayjs from "dayjs";
|
||||
import { getAdminMaintenanceDetail, updateAdminMaintenance } from "@/service/modular/admin";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
hospitalId: { type: [String, Number], default: "" },
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue", "saved"]);
|
||||
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const formRef = ref();
|
||||
const detail = ref({});
|
||||
|
||||
const form = reactive({
|
||||
renewal: "",
|
||||
contractSigned: "",
|
||||
paymentPaid: "",
|
||||
visited: "",
|
||||
visitResult: "",
|
||||
remark: "",
|
||||
});
|
||||
|
||||
const rules = {};
|
||||
|
||||
const formatDate = (v) => (v ? dayjs(v).format("YYYY-MM-DD") : "—");
|
||||
|
||||
const categoryTagType = (c) => ({ A: "", B: "success", C: "warning" })[c] || "info";
|
||||
|
||||
const loadDetail = async (id) => {
|
||||
if (!id) {
|
||||
detail.value = {};
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const data = await getAdminMaintenanceDetail(id);
|
||||
detail.value = data || {};
|
||||
Object.assign(form, {
|
||||
renewal: data?.renewal || "",
|
||||
contractSigned: data?.contractSigned || "",
|
||||
paymentPaid: data?.paymentPaid || "",
|
||||
visited: data?.visited || "",
|
||||
visitResult: data?.visitResult || "",
|
||||
remark: data?.remark || "",
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onOpen = () => {
|
||||
loadDetail(props.hospitalId);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [props.modelValue, props.hospitalId],
|
||||
([vis, id]) => {
|
||||
if (vis) loadDetail(id);
|
||||
},
|
||||
);
|
||||
|
||||
const closeDialog = () => emit("update:modelValue", false);
|
||||
|
||||
const onSubmit = async () => {
|
||||
submitting.value = true;
|
||||
try {
|
||||
const payload = {};
|
||||
["renewal", "contractSigned", "paymentPaid", "visited", "visitResult", "remark"].forEach((k) => {
|
||||
const v = form[k];
|
||||
if (v !== "" && v !== null && v !== undefined) payload[k] = v;
|
||||
});
|
||||
await updateAdminMaintenance(props.hospitalId, payload);
|
||||
ElMessage.success("保存成功");
|
||||
emit("saved");
|
||||
closeDialog();
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.maintenance-detail-dialog {
|
||||
width: 700px;
|
||||
|
||||
.readonly-val {
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.thin-divider {
|
||||
margin: 8px 0 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<el-dialog :model-value="modelValue" class="report-download-dialog" top="20vh" :close-on-click-modal="true"
|
||||
@update:model-value="(v) => $emit('update:modelValue', v)" @closed="onClosed">
|
||||
<div class="body-wrap">
|
||||
<div class="body-header">
|
||||
<div class="body-title">选择下载格式</div>
|
||||
<img src="@/assets/image/close-btn.svg" alt="" class="close-btn" @click="closeDialog" />
|
||||
</div>
|
||||
|
||||
<div class="format-grid">
|
||||
<div v-for="fmt in formats" :key="fmt.key" class="format-card" :class="{ active: selected === fmt.key }"
|
||||
@click="onPick(fmt)">
|
||||
<div class="format-icon">
|
||||
<img :src="fmt.icon" :alt="fmt.label" />
|
||||
</div>
|
||||
<div class="format-label">{{ fmt.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { downloadAdminReport } from "@/service/modular/admin";
|
||||
import iconWord from "@/assets/image/admin/reports/word-icon.svg";
|
||||
import iconPdf from "@/assets/image/admin/reports/pdf-icon.svg";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
reportId: { type: [String, Number], default: "" },
|
||||
});
|
||||
const emit = defineEmits(["update:modelValue", "downloaded", "exportPdf", "exportDocx"]);
|
||||
|
||||
const formats = [
|
||||
{ key: "word", label: "下载WORD格式", icon: iconWord, ext: "doc" },
|
||||
{ key: "pdf", label: "下载PDF格式", icon: iconPdf, ext: "pdf" },
|
||||
];
|
||||
|
||||
const selected = ref("");
|
||||
const downloading = ref(false);
|
||||
|
||||
const closeDialog = () => emit("update:modelValue", false);
|
||||
const onClosed = () => {
|
||||
selected.value = "";
|
||||
};
|
||||
|
||||
const downloadBlob = (blob, fileName) => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = fileName || "download";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
setTimeout(() => URL.revokeObjectURL(url), 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* 选中某个格式:
|
||||
* - pdf: 触发 exportPdf 事件,由父组件调用详情框(ref)执行前端截图导出
|
||||
* - word: 走后端 blob 下载,保持原逻辑不变
|
||||
*/
|
||||
const onPick = (fmt) => {
|
||||
selected.value = fmt.key;
|
||||
if (fmt.key === "pdf") {
|
||||
if (!props.reportId) {
|
||||
ElMessage.warning("报告不存在");
|
||||
return;
|
||||
}
|
||||
emit("exportPdf", { reportId: props.reportId });
|
||||
closeDialog();
|
||||
return;
|
||||
}
|
||||
onConfirm();
|
||||
};
|
||||
|
||||
const onConfirm = async () => {
|
||||
if (!props.reportId) {
|
||||
ElMessage.warning("报告不存在");
|
||||
return;
|
||||
}
|
||||
const fmt = formats.find((f) => f.key === selected.value);
|
||||
downloading.value = true;
|
||||
try {
|
||||
// Word 格式:改用前端截图导出(emit 给父组件,由 ReportDetailDialog 处理)
|
||||
if (selected.value === "word") {
|
||||
emit("exportDocx", { reportId: props.reportId });
|
||||
closeDialog();
|
||||
return;
|
||||
}
|
||||
const blob = await downloadAdminReport(props.reportId, selected.value);
|
||||
const fileName = `report-${props.reportId}.${fmt?.ext || selected.value}`;
|
||||
downloadBlob(blob, fileName);
|
||||
ElMessage.success("下载已开始");
|
||||
emit("downloaded", { format: selected.value });
|
||||
closeDialog();
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.message || "下载失败");
|
||||
} finally {
|
||||
downloading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.report-download-dialog {
|
||||
overflow: hidden;
|
||||
|
||||
.format-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.format-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
width: 358px;
|
||||
height: 158px;
|
||||
background: #F9FAFF;
|
||||
border-radius: 16px 16px 16px 16px;
|
||||
border: 1px solid #E0E0E0;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #4f8cff;
|
||||
background: #f0f6ff;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: #4f8cff;
|
||||
background: #f0f6ff;
|
||||
box-shadow: 0 4px 14px rgba(79, 140, 255, 0.18);
|
||||
}
|
||||
|
||||
.format-icon {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.format-label {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
color: #1D2129;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<el-dialog :model-value="modelValue" class="report-remark-dialog" top="15vh" :close-on-click-modal="false"
|
||||
@update:model-value="(v) => $emit('update:modelValue', v)" @open="onOpen">
|
||||
<div class="body-wrap">
|
||||
<div class="body-header">
|
||||
<div class="body-title">编辑备注</div>
|
||||
<img src="@/assets/image/close-btn.svg" alt="" class="close-btn" @click="closeDialog" />
|
||||
</div>
|
||||
|
||||
<el-form :model="form" class="remark-form">
|
||||
<el-form-item label="报告信息">
|
||||
<el-input :model-value="reportInfo" disabled readonly />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="发送状态">
|
||||
<el-select v-model="form.sent" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="已发客户" :value="true" />
|
||||
<el-option label="待发客户" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注内容">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="5" placeholder="请输入备注内容" maxlength="500"
|
||||
show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="body-footer">
|
||||
<el-button @click="closeDialog">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="onSubmit">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { updateAdminReportRemark } from "@/service/modular/admin";
|
||||
|
||||
const { t: $t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
reportRow: { type: Object, default: null },
|
||||
});
|
||||
const emit = defineEmits(["update:modelValue", "saved"]);
|
||||
|
||||
const form = reactive({ remark: "", sent: "" });
|
||||
const submitting = ref(false);
|
||||
const reportInfo = ref("");
|
||||
|
||||
const reset = () => {
|
||||
form.remark = "";
|
||||
form.sent = "";
|
||||
reportInfo.value = "";
|
||||
};
|
||||
|
||||
const buildReportInfo = (row) => {
|
||||
const hospital = row?.hospital;
|
||||
const period = row?.period || "";
|
||||
const typeText = row?.type === "annual" ? "报告" : " 季度报告";
|
||||
return `${hospital} - ${period}${typeText}`;
|
||||
};
|
||||
|
||||
const onOpen = () => {
|
||||
if (!props.reportRow) return;
|
||||
reset();
|
||||
const row = props.reportRow;
|
||||
console.log('row',row);
|
||||
|
||||
form.remark = row?.remark ?? "";
|
||||
form.sent = row?.sent ?? "";
|
||||
reportInfo.value = buildReportInfo(row);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(v) => {
|
||||
if (v) onOpen();
|
||||
else reset();
|
||||
},
|
||||
);
|
||||
|
||||
const closeDialog = () => emit("update:modelValue", false);
|
||||
|
||||
const onSubmit = async () => {
|
||||
const reportId = props.reportRow?.id || "";
|
||||
if (!reportId) {
|
||||
ElMessage.warning("报告不存在");
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
try {
|
||||
await updateAdminReportRemark(reportId, {
|
||||
remark: form.remark,
|
||||
sent: form.sent,
|
||||
});
|
||||
ElMessage.success('备注更新成功');
|
||||
emit("saved");
|
||||
closeDialog();
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.report-remark-dialog {
|
||||
width: 800px;
|
||||
|
||||
.el-form-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.remark-form {
|
||||
.el-form-item__label {
|
||||
font-weight: 500;
|
||||
color: #1d2129;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||