fix: 更新诸多bug

This commit is contained in:
speakeloudest 2026-01-08 02:01:21 -08:00
parent 9563a81a6e
commit 8477c2e343
96 changed files with 1338 additions and 5695 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 138 KiB

View File

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" fill="none" viewBox="0 0 44 44">
<path fill="#1797FF" d="M38.565 18.206c0-6.948-4.156-12.93-10.115-15.585a17.032 17.032 0 0 0-6.991-1.487c-9.445 0-17.106 7.657-17.106 17.106 0 4.168 1.49 7.984 3.966 10.953.038.052.077.103.12.15l.047.052c.086.099.168.193.258.292l.004-.004L20.075 42.07c.125.137.27.249.425.34.71.476 1.68.373 2.278-.276l11.038-12.078.005.004c.223-.232.442-.473.653-.722l.004-.004c.03-.03.056-.065.082-.099a17.044 17.044 0 0 0 4.009-11.004v-.013c-.005-.005-.005-.009-.005-.013Z" opacity=".2"/>
<path fill="#1797FF" d="M21.5 6C14.6 6 9 11.6 9 18.5S14.6 31 21.5 31 34 25.4 34 18.5 28.4 6 21.5 6Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 685 B

View File

@ -1,13 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_81_10597)">
<g id="Frame_2">
<path id="Vector" d="M17.3125 16.75H2.6875C1.75712 16.75 1 15.9929 1 15.0625V4.9375C1 4.00712 1.75712 3.25 2.6875 3.25H17.3125C18.2429 3.25 19 4.00712 19 4.9375V15.0625C19 15.9929 18.2429 16.75 17.3125 16.75ZM2.6875 4.375C2.53832 4.375 2.39524 4.43426 2.28975 4.53975C2.18426 4.64524 2.125 4.78832 2.125 4.9375V15.0625C2.125 15.2117 2.18426 15.3548 2.28975 15.4602C2.39524 15.5657 2.53832 15.625 2.6875 15.625H17.3125C17.6219 15.625 17.875 15.3719 17.875 15.0625V4.9375C17.875 4.78832 17.8157 4.64524 17.7102 4.53975C17.6048 4.43426 17.4617 4.375 17.3125 4.375H2.6875Z" fill="#ABABAB"/>
<path id="Vector_2" d="M10 10.7964C9.88906 10.7964 9.7806 10.7635 9.68837 10.7019L4.06337 6.95224C3.99954 6.91238 3.94435 6.86013 3.90107 6.79856C3.85779 6.73699 3.82731 6.66736 3.81143 6.5938C3.79555 6.52023 3.7946 6.44423 3.80862 6.37029C3.82264 6.29635 3.85136 6.22598 3.89307 6.16334C3.93478 6.1007 3.98863 6.04706 4.05144 6.00561C4.11426 5.96416 4.18475 5.93574 4.25875 5.92202C4.33274 5.9083 4.40874 5.90957 4.48223 5.92576C4.55573 5.94194 4.62523 5.97271 4.68662 6.01624L10 9.55774L15.3134 6.01624C15.4375 5.93875 15.5869 5.91267 15.73 5.94355C15.873 5.97443 15.9984 6.05983 16.0795 6.18162C16.1606 6.30342 16.191 6.45203 16.1643 6.59591C16.1377 6.73978 16.056 6.8676 15.9366 6.95224L10.3116 10.7019C10.2196 10.764 10.111 10.797 10 10.7964Z" fill="#ABABAB"/>
</g>
</g>
<defs>
<clipPath id="clip0_81_10597">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,9 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Close-one (&#229;&#133;&#179;&#233;&#151;&#173;)">
<g id="Close-one (&#229;&#133;&#179;&#233;&#151;&#173;)_2">
<path id="Vector" d="M9 16.5C13.1421 16.5 16.5 13.1421 16.5 9C16.5 4.85786 13.1421 1.5 9 1.5C4.85786 1.5 1.5 4.85786 1.5 9C1.5 13.1421 4.85786 16.5 9 16.5Z" fill="#CFCFCF" stroke="#CFCFCF" stroke-width="1.125" stroke-linejoin="round"/>
<path id="Vector_2" d="M11.1213 6.87891L6.87866 11.1215" stroke="white" stroke-width="1.125" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_3" d="M6.87866 6.87891L11.1213 11.1215" stroke="white" stroke-width="1.125" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 751 B

View File

@ -1,10 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_246_5226)">
<path id="Vector" d="M9.99884 17.9997C7.59311 17.9997 3.11719 14.4141 3.11719 11.0227V4.54952C3.11719 4.39249 3.24869 4.26563 3.41152 4.26234L3.80139 4.25576C3.81628 4.25576 5.34461 4.22502 6.92013 3.60115C8.53703 2.96298 9.57919 2.22714 9.59021 2.2196L9.81976 2.05677C9.87194 2.02017 9.93413 2.00052 9.99787 2.00049C10.0612 2.00008 10.123 2.01942 10.1748 2.0558L10.4088 2.21999C10.4194 2.22753 11.4626 2.96259 13.0775 3.60154C14.6546 4.2254 16.1829 4.25615 16.1986 4.25615L16.5854 4.26273C16.7484 4.26601 16.8795 4.39288 16.8795 4.54991L16.8828 11.0231C16.8828 14.4141 12.4061 18.0001 9.99825 18.0001L9.99884 17.9997ZM15.8211 5.30334C15.2295 5.25886 13.9849 5.10608 12.6919 4.59419C11.3707 4.07205 10.3993 3.48473 9.99884 3.2254C9.59949 3.48377 8.62753 4.07127 7.30671 4.59419C6.01605 5.10492 4.77489 5.2577 4.1752 5.30334V11.0227C4.1752 13.6824 8.07484 16.9334 9.99884 16.9334C10.7445 16.9334 12.1976 16.3283 13.626 14.9995C15.0029 13.7197 15.8248 12.2328 15.8248 11.0231L15.8211 5.30334ZM9.44691 12.1105C9.39833 12.1603 9.34025 12.1999 9.2761 12.2269C9.21196 12.2538 9.14306 12.2677 9.07348 12.2676C9.00371 12.2675 8.93464 12.2537 8.87027 12.2267C8.8059 12.1998 8.74752 12.1604 8.69851 12.1107L7.10984 10.5118C7.01064 10.4114 6.95501 10.2759 6.95501 10.1347C6.95501 9.99353 7.01064 9.85804 7.10984 9.7576C7.15877 9.70799 7.21708 9.6686 7.28137 9.64171C7.34566 9.61482 7.41465 9.60097 7.48433 9.60097C7.55402 9.60097 7.62301 9.61482 7.68729 9.64171C7.75158 9.6686 7.80989 9.70799 7.85882 9.7576L9.07348 10.9792L12.1402 7.89026C12.189 7.84057 12.2473 7.8011 12.3116 7.77416C12.3758 7.74721 12.4448 7.73334 12.5145 7.73334C12.5842 7.73334 12.6532 7.74721 12.7174 7.77416C12.7817 7.8011 12.8399 7.84057 12.8888 7.89026C12.9886 7.99039 13.0447 8.12599 13.0447 8.26737C13.0447 8.40874 12.9886 8.54435 12.8888 8.64447L9.44691 12.1105Z" fill="#ABABAB"/>
</g>
<defs>
<clipPath id="clip0_246_5226">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,10 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_81_10600)">
<path id="Vector" d="M5.54545 7.6V6.2C5.54545 3.8802 7.53939 2 10 2C12.4606 2 14.4545 3.8792 14.4545 6.2V7.6H14.6669C15.9549 7.6 17 8.5862 17 9.8V15.8C17 17.0154 15.9566 18 14.6669 18H5.33333C4.04512 18 3 17.0138 3 15.8V9.8C3 8.5846 4.04342 7.6 5.33312 7.6H5.54545ZM6.81818 7.6H13.1818V6.2C13.1818 4.542 11.7578 3.2 10 3.2C8.24236 3.2 6.81818 4.543 6.81818 6.2V7.6ZM4.27273 9.8V15.8C4.27273 16.3512 4.7483 16.8 5.33312 16.8H14.6667C14.806 16.8002 14.944 16.7744 15.0727 16.7242C15.2015 16.674 15.3185 16.6003 15.417 16.5075C15.5155 16.4146 15.5936 16.3043 15.6469 16.1829C15.7001 16.0615 15.7274 15.9314 15.7273 15.8V9.8C15.7273 9.2488 15.2517 8.8 14.6669 8.8H5.33333C5.19401 8.79984 5.05601 8.8256 4.92726 8.8758C4.7985 8.926 4.68152 8.99965 4.583 9.09254C4.48448 9.18543 4.40636 9.29573 4.35312 9.41713C4.29988 9.53853 4.27256 9.66863 4.27273 9.8Z" fill="#ABABAB"/>
</g>
<defs>
<clipPath id="clip0_81_10600">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,10 +0,0 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_408_56)">
<path d="M47.1582 14.4283C47.1582 13.8368 47.4684 13.2886 47.9757 12.9842L61.4494 4.89891C62.5721 4.22529 64.0003 5.03389 64.0003 6.34306V20.2103C64.0003 21.1405 63.2461 21.8945 62.3161 21.8945H48.8424C47.9124 21.8945 47.1582 21.1405 47.1582 20.2103V14.4283Z" fill="#455FE9"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.3966 26.4593C23.8893 26.7637 23.5789 27.3118 23.5789 27.9034V43.7921L23.5789 45.4751L23.5789 45.4763L23.5789 48.0001C23.5789 48.9301 22.8249 49.6001 21.8947 49.6001H18.5263C17.5962 49.6001 16.8421 48.9301 16.8421 48.0001V45.4751V34.976C16.8421 33.6669 15.4139 32.8583 14.2913 33.5319L0.817607 41.6171C0.310362 41.9216 0 42.4696 0 43.0615V60.6342C0 61.5642 0.754048 62.3184 1.68421 62.3184H15.1579C16.0881 62.3184 16.8421 61.5642 16.8421 60.6342V58.9488V56.8314C16.8421 55.9011 17.5962 55.3376 18.5263 55.3376H21.8947C22.8249 55.3376 23.5789 55.9011 23.5789 56.8314V58.9488L23.5789 60.6342C23.5789 61.5642 24.333 62.3184 25.2632 62.3184H38.7368C39.6669 62.3184 40.4211 61.5642 40.4211 60.6342V45.4763V43.7921V19.8182C40.4211 18.509 38.9928 17.7004 37.8701 18.374L24.3966 26.4593Z" fill="#455FE9"/>
<path d="M47.1592 28.7999V60.7999C47.1592 61.6836 47.9132 62.3999 48.8434 62.3999H62.3171C63.2472 62.3999 64.0013 61.6836 64.0013 60.7999V28.7999C64.0013 27.9162 63.2472 27.1999 62.3171 27.1999H48.8434C47.9132 27.1999 47.1592 27.9162 47.1592 28.7999Z" fill="#455FE9"/>
</g>
<defs>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="30" height="30" viewBox="0 0 30 30"><defs><clipPath id="master_svg0_0_54108"><rect x="0" y="0" width="30" height="30" rx="0"/></clipPath><clipPath id="master_svg1_0_54109"><rect x="3" y="3" width="24" height="24" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_0_54108)"><g clip-path="url(#master_svg1_0_54109)"><g><path d="M15.560000085830689,25.78800003528595C9.824000085830688,25.78800003528595,5.1600000858306885,21.12400003528595,5.1600000858306885,15.38800003528595C5.1600000858306885,9.65200003528595,9.824000085830688,4.98800005872345,15.560000085830689,4.98800003528595C21.296000085830688,4.98800001184845,25.96000008583069,9.65200003528595,25.96000008583069,15.38800003528595C25.96000008583069,21.12400003528595,21.29200008583069,25.78800003528595,15.560000085830689,25.78800003528595ZM15.560000085830689,6.588000035285949C10.708000085830689,6.588000035285949,6.760000085830688,10.53600003528595,6.760000085830688,15.38800003528595C6.760000085830688,20.240000035285952,10.708000085830689,24.18800003528595,15.560000085830689,24.18800003528595C20.41200008583069,24.18800003528595,24.360000085830688,20.240000035285952,24.360000085830688,15.38800003528595C24.360000085830688,10.53600003528595,20.41200008583069,6.588000035285949,15.560000085830689,6.588000035285949Z" fill="#FF3C3C" fill-opacity="1"/></g><g><path d="M15.559999228881836,16.18800030517578C15.119999228881836,16.18800030517578,14.760000228881836,15.828000305175781,14.760000228881836,15.388000305175781C14.760000228881836,15.388000305175781,14.760000228881836,10.480000305175782,14.760000228881836,10.480000305175782C14.760000228881836,10.04000030517578,15.119999228881836,9.680000328613282,15.559999228881836,9.680000305175783C16.000000228881834,9.68000028173828,16.360000228881837,10.04000030517578,16.360000228881837,10.480000305175782C16.360000228881837,10.480000305175782,16.360000228881837,15.384000305175782,16.360000228881837,15.384000305175782C16.360000228881837,15.828000305175781,16.000000228881834,16.18800030517578,15.559999228881836,16.18800030517578Z" fill="#FF3C3C" fill-opacity="1"/></g><g><path d="M14.347999572753906,18.707999336242676C14.347999572753908,19.029439336242675,14.475691572753906,19.337719336242674,14.702985572753906,19.565009336242674C14.930280572753906,19.792309336242674,15.238556572753906,19.919999336242675,15.559999572753906,19.919999336242675C15.881439572753907,19.919999336242675,16.189719572753905,19.792309336242674,16.417009572753905,19.565009336242674C16.644309572753905,19.337719336242674,16.771999572753906,19.029439336242675,16.771999572753906,18.707999336242676C16.771999572753906,18.386557336242674,16.644309572753905,18.078280336242678,16.417009572753905,17.850986336242677C16.189719572753905,17.623692336242677,15.881439572753907,17.495999336242676,15.559999572753906,17.495999336242676C15.238556572753906,17.495999336242676,14.930280572753906,17.623692336242677,14.702985572753906,17.850986336242677C14.475691572753906,18.078280336242678,14.347999572753908,18.386557336242674,14.347999572753906,18.707999336242676Z" fill="#FF3C3C" fill-opacity="1"/></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="40" height="40" viewBox="0 0 40 40"><defs><clipPath id="master_svg0_0_54137"><rect x="0" y="0" width="40" height="40" rx="0"/></clipPath><clipPath id="master_svg1_0_54138"><rect x="6" y="6" width="28" height="28" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_0_54137)"><g clip-path="url(#master_svg1_0_54138)"><g><path d="M28.633299713897706,7.400000095367432C28.166699713897707,7.400000095367432,27.699999713897704,7.750000095367431,27.699999713897704,8.333333095367431C27.699999713897704,8.333333095367431,27.699999713897704,12.650000095367432,27.699999713897704,12.650000095367432C25.949999713897704,10.200000095367432,23.149999713897706,8.683330095367431,19.999999713897704,8.683330095367431C14.749999713897704,8.683330095367431,10.549999713897705,13.000000095367431,10.549999713897705,18.13330009536743C10.549999713897705,18.13330009536743,10.549999713897705,23.85000009536743,10.549999713897705,23.85000009536743C10.549999713897705,25.483300095367433,11.949999713897705,26.88330009536743,13.583329713897704,26.88330009536743C15.216669713897705,26.88330009536743,16.616669713897707,25.483300095367433,16.616669713897707,23.85000009536743C16.616669713897707,23.85000009536743,16.616669713897707,21.40000009536743,16.616669713897707,21.40000009536743C16.616669713897707,19.766700095367433,15.216669713897705,18.36670009536743,13.583329713897704,18.36670009536743C13.116669713897705,18.36670009536743,12.649999713897705,18.483300095367433,12.299999713897705,18.716700095367433C12.299999713897705,18.716700095367433,12.299999713897705,18.016700095367433,12.299999713897705,18.016700095367433C12.299999713897705,13.700000095367432,15.799999713897705,10.316670095367432,19.999999713897704,10.316670095367432C24.199999713897704,10.316670095367432,27.699999713897704,13.816670095367432,27.699999713897704,18.016700095367433C27.699999713897704,18.016700095367433,27.699999713897704,18.83330009536743,27.699999713897704,18.83330009536743C27.233299713897704,18.60000009536743,26.766699713897705,18.36670009536743,26.183299713897703,18.36670009536743C24.549999713897705,18.36670009536743,23.149999713897706,19.766700095367433,23.149999713897706,21.40000009536743C23.149999713897706,21.40000009536743,23.149999713897706,23.85000009536743,23.149999713897706,23.85000009536743C23.149999713897706,25.483300095367433,24.549999713897705,26.88330009536743,26.183299713897703,26.88330009536743C26.183299713897703,26.88330009536743,26.416699713897707,26.88330009536743,26.416699713897707,26.88330009536743C25.249999713897704,28.750000095367433,23.266699713897705,30.03330009536743,20.933299713897703,30.266700095367433C20.466669713897705,30.38330009536743,20.116669713897707,30.733300095367433,20.116669713897707,31.200000095367432C20.116669713897707,31.666700095367432,20.583299713897706,32.01670009536743,20.933299713897703,32.01670009536743C20.933299713897703,32.01670009536743,21.049999713897705,32.01670009536743,21.049999713897705,32.01670009536743C25.599999713897706,31.433300095367432,28.983299713897704,27.700000095367432,29.333299713897706,23.266700095367433C29.333299713897706,23.266700095367433,29.333299713897706,8.216667095367432,29.333299713897706,8.216667095367432C29.449999713897704,7.750000095367431,29.099999713897706,7.400000095367432,28.633299713897706,7.400000095367432ZM13.583329713897704,20.11670009536743C14.283329713897706,20.11670009536743,14.866669713897705,20.700000095367432,14.866669713897705,21.40000009536743C14.866669713897705,21.40000009536743,14.866669713897705,23.85000009536743,14.866669713897705,23.85000009536743C14.866669713897705,24.55000009536743,14.283329713897706,25.13330009536743,13.583329713897704,25.13330009536743C12.883329713897705,25.13330009536743,12.299999713897705,24.55000009536743,12.299999713897705,23.85000009536743C12.299999713897705,23.85000009536743,12.299999713897705,21.516700095367433,12.299999713897705,21.516700095367433C12.299999713897705,20.700000095367432,12.883329713897705,20.11670009536743,13.583329713897704,20.11670009536743ZM27.583299713897706,23.85000009536743C27.583299713897706,24.55000009536743,26.999999713897704,25.13330009536743,26.299999713897705,25.13330009536743C25.599999713897706,25.13330009536743,25.016699713897705,24.55000009536743,25.016699713897705,23.85000009536743C25.016699713897705,23.85000009536743,25.016699713897705,21.40000009536743,25.016699713897705,21.40000009536743C25.016699713897705,20.700000095367432,25.599999713897706,20.11670009536743,26.299999713897705,20.11670009536743C26.999999713897704,20.11670009536743,27.583299713897706,20.700000095367432,27.583299713897706,21.40000009536743C27.583299713897706,21.40000009536743,27.583299713897706,23.85000009536743,27.583299713897706,23.85000009536743Z" fill="#333333" fill-opacity="1"/></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="26" height="26" viewBox="0 0 26 26"><defs><clipPath id="master_svg0_0_54219"><rect x="5" y="5" width="16" height="16" rx="0"/></clipPath></defs><g><g><g><rect x="0" y="0" width="26" height="26" rx="8" fill="#FF9317" fill-opacity="1"/></g><g clip-path="url(#master_svg0_0_54219)"><g><path d="M7.337497416553497,7.684375047683716C7.337497416553497,7.684375047683716,18.1484374165535,7.684375047683716,18.1484374165535,7.684375047683716C18.885937416553496,7.684375047683716,19.490637416553497,8.273437047683716,19.500037416553496,8.995315047683716C19.500037416553496,8.995315047683716,12.745317416553497,12.703125047683717,12.745317416553497,12.703125047683717C12.745317416553497,12.703125047683717,5.992187436553498,8.999995047683715,5.992187436553498,8.999995047683715C5.9984374165534975,8.275000047683715,6.598437416553497,7.684375047683716,7.337497416553497,7.684375047683716ZM5.992187436553498,10.418745047683716C5.992187436553498,10.418745047683716,5.985937416553497,16.975005047683716,5.985937416553497,16.975005047683716C5.985937416553497,17.704675047683715,6.5937504165534975,18.301575047683716,7.337497416553497,18.301575047683716C7.337497416553497,18.301575047683716,18.1484374165535,18.301575047683716,18.1484374165535,18.301575047683716C18.8922374165535,18.301575047683716,19.500037416553496,17.704675047683715,19.500037416553496,16.975005047683716C19.500037416553496,16.975005047683716,19.500037416553496,10.415625047683715,19.500037416553496,10.415625047683715C19.500037416553496,10.415625047683715,12.904687416553497,13.949995047683716,12.904687416553497,13.949995047683716C12.803127416553497,14.004685047683715,12.681247416553497,14.004685047683715,12.581247416553497,13.949995047683716C12.581247416553497,13.949995047683716,5.992187436553498,10.418745047683716,5.992187436553498,10.418745047683716Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="18" height="18" viewBox="0 0 18 18"><defs><clipPath id="master_svg0_0_54206"><rect x="0" y="0" width="18" height="18" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_0_54206)"><g><path d="M15.231050173568725,8.453999919891357C14.901050173568725,8.465999919891358,14.643050173568726,8.744999919891358,14.655050173568725,9.077999919891358C14.717950173568726,10.664999919891358,14.112050173568726,12.218999919891358,12.990050173568726,13.340999919891358C11.913000173568726,14.417999919891358,10.482000173568725,15.011999919891357,8.958000173568726,15.011999919891357C7.434000173568726,15.011999919891357,6.003000173568726,14.417999919891358,4.929000173568726,13.340999919891358C2.7060001735687256,11.117999919891357,2.7060001735687256,7.502999919891358,4.929000173568726,5.279999919891358C6.006000173568726,4.202999919891358,7.437000173568726,3.6089999198913576,8.958000173568726,3.6089999198913576C9.288000173568726,3.6089999198913576,9.558000173568725,3.3389999198913576,9.558000173568725,3.0089999198913575C9.558000173568725,2.6789999198913574,9.288000173568726,2.4089999374694573,8.958000173568726,2.4089999198913574C7.116000173568725,2.4089999198913574,5.382000173568725,3.1259999198913575,4.080000173568726,4.430999919891358C1.3890001735687256,7.1219999198913575,1.3890001735687256,11.498999919891357,4.080000173568726,14.189999919891358C5.382000173568725,15.491999919891358,7.116000173568725,16.21199991989136,8.958000173568726,16.21199991989136C10.800000173568726,16.21199991989136,12.534050173568726,15.494999919891358,13.836050173568726,14.189999919891358C14.509250173568725,13.515699919891357,15.035850173568726,12.709599919891357,15.382750173568725,11.822119919891357C15.729750173568725,10.934679919891357,15.889450173568726,9.985109919891357,15.852050173568726,9.032999919891356C15.842950173568726,8.699999919891358,15.566950173568726,8.444999919891357,15.231050173568725,8.453999919891357Z" fill="#333333" fill-opacity="1"/></g><g><path d="M15.294000695495605,2.433000087738038C15.294000695495605,2.433000087738038,12.363000695495606,2.433000087738038,12.363000695495606,2.433000087738038C12.033000695495605,2.433000087738038,11.763000695495606,2.703000087738037,11.763000695495606,3.033000087738037C11.763000695495606,3.3630000877380373,12.033000695495605,3.6330000877380373,12.363000695495606,3.6330000877380373C12.363000695495606,3.6330000877380373,13.899000695495605,3.6330000877380373,13.899000695495605,3.6330000877380373C13.899000695495605,3.6330000877380373,9.585000695495605,7.944000087738037,9.585000695495605,7.944000087738037C9.501154495495605,8.027970087738037,9.444031395495605,8.134870087738037,9.420827495495605,8.251240087738037C9.397623495495605,8.367620087738036,9.409375748495606,8.488250087738038,9.454605095495605,8.597960087738038C9.499834395495606,8.707660087738038,9.576517695495605,8.801530087738037,9.674994695495606,8.867730087738037C9.773471695495605,8.933940087738037,9.889338695495605,8.969520087738037,10.008000695495605,8.970000087738036C10.161000695495606,8.970000087738036,10.314000695495606,8.910000087738037,10.431000695495605,8.793000087738037C10.431000695495605,8.793000087738037,14.691000695495607,4.533000087738037,14.691000695495607,4.533000087738037C14.691000695495607,4.533000087738037,14.691000695495607,5.961000087738038,14.691000695495607,5.961000087738038C14.691000695495607,6.291000087738038,14.961000695495606,6.561000087738037,15.291000695495605,6.561000087738037C15.621000695495606,6.561000087738037,15.891000695495606,6.291000087738038,15.891000695495606,5.961000087738038C15.891000695495606,5.961000087738038,15.891000695495606,3.030000087738037,15.891000695495606,3.030000087738037C15.894000695495606,2.700000087738037,15.624000695495607,2.4330000701599372,15.294000695495605,2.433000087738038Z" fill="#333333" fill-opacity="1"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="26" height="26" viewBox="0 0 26 26"><defs><clipPath id="master_svg0_0_54267"><rect x="6" y="5" width="15" height="15" rx="0"/></clipPath></defs><g><g><rect x="0" y="0" width="26" height="26" rx="8" fill="#FF9317" fill-opacity="1"/></g><g clip-path="url(#master_svg0_0_54267)"><g><path d="M13.5,5.9375C17.37895,5.9375,20.53125,8.871410000000001,20.53125,12.5C20.53125,16.1281,17.37895,19.0625,13.50141,19.0625C12.28315,19.0664,11.08285,18.768900000000002,10.00734,18.1967C10.00734,18.1967,9.76078,18.064500000000002,9.76078,18.064500000000002C9.76078,18.064500000000002,8.25141,18.7081,8.25141,18.7081C8.17543,18.7405,8.09223,18.752299999999998,8.01025,18.7422C7.9282699999999995,18.732100000000003,7.85042,18.700499999999998,7.78457,18.6506C7.71872,18.6008,7.66721,18.534399999999998,7.63527,18.458199999999998C7.60332,18.3821,7.59207,18.2988,7.60266,18.216900000000003C7.60266,18.216900000000003,7.61203,18.1658,7.61203,18.1658C7.61203,18.1658,7.995,16.5903,7.995,16.5903C7.995,16.5903,7.83094,16.382199999999997,7.83094,16.382199999999997C7.83094,16.382199999999997,7.73719,16.2613,7.73719,16.2613C6.91125,15.16016,6.46875,13.85656,6.46875,12.5C6.46875,8.87188,9.62109,5.9375,13.5,5.9375ZM10.92187,11.5625C10.92187,11.5625,10.85437,11.56578,10.85437,11.56578C10.67465,11.583210000000001,10.508510000000001,11.66911,10.39039,11.80569C10.27228,11.94227,10.21123,12.11905,10.21991,12.29942C10.22859,12.47978,10.30632,12.64989,10.43701,12.7745C10.567689999999999,12.89911,10.741299999999999,12.96866,10.92187,12.96875C10.92187,12.96875,10.98938,12.96547,10.98938,12.96547C11.1691,12.948039999999999,11.335239999999999,12.86214,11.45336,12.72556C11.57147,12.58898,11.63252,12.4122,11.623840000000001,12.23184C11.61516,12.05147,11.53743,11.88136,11.40674,11.75675C11.276060000000001,11.63215,11.102450000000001,11.56259,10.92187,11.5625ZM16.54685,11.5625C16.54685,11.5625,16.47935,11.56578,16.47935,11.56578C16.29965,11.583210000000001,16.13351,11.66911,16.01539,11.80569C15.89728,11.94227,15.83623,12.11905,15.84491,12.29942C15.85359,12.47978,15.93132,12.64989,16.06201,12.7745C16.19269,12.89911,16.366300000000003,12.96866,16.54685,12.96875C16.54685,12.96875,16.61435,12.96547,16.61435,12.96547C16.794150000000002,12.948039999999999,16.960250000000002,12.86214,17.07835,12.72556C17.19645,12.58898,17.257550000000002,12.4122,17.248849999999997,12.23184C17.24015,12.05147,17.16245,11.88136,17.031750000000002,11.75675C16.901049999999998,11.63215,16.727449999999997,11.56259,16.54685,11.5625ZM13.73437,11.5625C13.73437,11.5625,13.666879999999999,11.56578,13.666879999999999,11.56578C13.48715,11.583210000000001,13.321010000000001,11.66911,13.20289,11.80569C13.08478,11.94227,13.02373,12.11905,13.032409999999999,12.29942C13.04109,12.47978,13.11882,12.64989,13.24951,12.7745C13.380189999999999,12.89911,13.553799999999999,12.96866,13.73437,12.96875C13.73437,12.96875,13.801870000000001,12.96547,13.801870000000001,12.96547C13.9816,12.948039999999999,14.147739999999999,12.86214,14.26586,12.72556C14.38397,12.58898,14.44502,12.4122,14.436340000000001,12.23184C14.42766,12.05147,14.34993,11.88136,14.21924,11.75675C14.088560000000001,11.63215,13.914950000000001,11.56259,13.73437,11.5625Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.0 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="26" height="26" viewBox="0 0 26 26">
<g>
<g>
<rect x="0" y="0" width="26" height="26" rx="8" fill="#0087CC" fill-opacity="1"/>
</g>
<g>
<path d="M20 7L5 13L8.5 14.5L15 10L11.5 15.5L17.5 19L20 7Z" fill="#FFFFFF" fill-opacity="1"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 433 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 43 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,10 +0,0 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_144_248)">
<path id="Vector" d="M16.7447 20.9744C16.7447 20.4054 16.3184 19.2213 14.9916 19.2213H13.2368C12.0846 19.2213 11.4837 20.055 11.4837 20.9744C11.4837 27.2078 11.4837 26.225 11.4837 26.225H7.4392C5.75119 26.225 4.44257 24.7807 4.44257 23.3032V15.1444H2.49655C1.65706 15.1444 1.2508 14.5606 1.08642 14.1908C1.02136 14.0424 0.743452 13.2486 1.72724 12.2289L11.9506 2.61121C12.4892 2.05366 13.2105 1.74609 13.9815 1.74609C14.7525 1.74609 15.4738 2.05366 16.0125 2.61177L26.4874 12.2226C26.4892 12.2249 26.4915 12.2272 26.4937 12.2289C27.477 13.2493 27.1991 14.0419 27.134 14.1909C26.9696 14.5607 26.5645 15.1445 25.7239 15.1445H23.7722V23.3033C23.7722 24.7807 22.4077 26.2251 20.7191 26.2251H16.7447C16.7447 26.225 16.7447 26.2107 16.7447 20.9744Z" fill="#DEDEDE"/>
</g>
<defs>
<clipPath id="clip0_144_248">
<rect width="28" height="28" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1017 B

View File

@ -1,10 +0,0 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_64_195)">
<path id="Vector" d="M16.7447 20.9744C16.7447 20.4054 16.3185 19.2213 14.9917 19.2213H13.2368C12.0847 19.2213 11.4838 20.055 11.4838 20.9744C11.4838 27.2078 11.4838 26.225 11.4838 26.225H7.43926C5.75125 26.225 4.44263 24.7807 4.44263 23.3032V15.1444H2.49661C1.65712 15.1444 1.25086 14.5606 1.08648 14.1908C1.02142 14.0424 0.743513 13.2486 1.7273 12.2289L11.9506 2.61121C12.4893 2.05366 13.2106 1.74609 13.9816 1.74609C14.7525 1.74609 15.4739 2.05366 16.0126 2.61177L26.4875 12.2226C26.4892 12.2249 26.4915 12.2272 26.4938 12.2289C27.477 13.2493 27.1991 14.0419 27.1341 14.1909C26.9697 14.5607 26.5646 15.1445 25.724 15.1445H23.7722V23.3033C23.7722 24.7807 22.4078 26.2251 20.7192 26.2251H16.7448C16.7447 26.225 16.7447 26.2107 16.7447 20.9744Z" fill="#1797FF"/>
</g>
<defs>
<clipPath id="clip0_64_195">
<rect width="28" height="28" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1016 B

View File

@ -1,12 +0,0 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_64_190)">
<g id="Frame_2">
<path id="Vector" d="M14.8727 16.9952V25.7225C14.8727 25.8371 14.8953 25.9506 14.9392 26.0565C14.983 26.1624 15.0473 26.2586 15.1283 26.3396C15.2094 26.4207 15.3056 26.485 15.4115 26.5288C15.5174 26.5727 15.6308 26.5952 15.7455 26.5952H21.8545C22.3175 26.5952 22.7614 26.4114 23.0888 26.084C23.4161 25.7567 23.6 25.3127 23.6 24.8498V16.9952C23.6 16.8806 23.5774 16.7672 23.5336 16.6613C23.4897 16.5554 23.4254 16.4592 23.3444 16.3781C23.2633 16.2971 23.1671 16.2328 23.0612 16.189C22.9554 16.1451 22.8419 16.1225 22.7273 16.1225H15.7455C15.6308 16.1225 15.5174 16.1451 15.4115 16.189C15.3056 16.2328 15.2094 16.2971 15.1283 16.3781C15.0473 16.4592 14.983 16.5554 14.9392 16.6613C14.8953 16.7672 14.8727 16.8806 14.8727 16.9952ZM12.2545 16.1225H5.27272C5.15811 16.1225 5.04463 16.1451 4.93874 16.189C4.83286 16.2328 4.73665 16.2971 4.65561 16.3781C4.57457 16.4592 4.51029 16.5554 4.46643 16.6613C4.42257 16.7672 4.4 16.8806 4.4 16.9952V24.8498C4.4 25.3127 4.58389 25.7567 4.91123 26.084C5.23856 26.4114 5.68253 26.5952 6.14545 26.5952H12.2545C12.3692 26.5952 12.4826 26.5727 12.5885 26.5288C12.6944 26.485 12.7906 26.4207 12.8717 26.3396C12.9527 26.2586 13.017 26.1624 13.0608 26.0565C13.1047 25.9506 13.1273 25.8371 13.1273 25.7225V16.9952C13.1273 16.8806 13.1047 16.7672 13.0608 16.6613C13.017 16.5554 12.9527 16.4592 12.8717 16.3781C12.7906 16.2971 12.6944 16.2328 12.5885 16.189C12.4826 16.1451 12.3692 16.1225 12.2545 16.1225ZM23.6 7.39525H21.8698C22.2827 6.90556 22.5594 6.31573 22.6719 5.68514C22.7728 5.10782 22.7332 4.5147 22.5567 3.95586C22.3801 3.39703 22.0716 2.88889 21.6573 2.47437C21.1025 1.92075 20.3847 1.55987 19.6094 1.44487C18.8341 1.32986 18.0424 1.4668 17.3508 1.83554C16.6265 2.21823 16.0727 2.86099 15.7249 3.60281L14 7.28048L12.2607 3.57314C11.9713 2.95612 11.5389 2.4063 10.9712 2.02972C9.44611 1.01823 7.53265 1.28397 6.34269 2.47437C5.92844 2.88893 5.62001 3.39707 5.44343 3.95589C5.26686 4.51472 5.22731 5.10782 5.32814 5.68514C5.44062 6.31573 5.71725 6.90556 6.13018 7.39525H4.4C3.93707 7.39525 3.49311 7.57914 3.16577 7.90648C2.83844 8.23381 2.65454 8.67778 2.65454 9.1407V12.6316C2.65454 13.0945 2.83844 13.5385 3.16577 13.8658C3.49311 14.1932 3.93707 14.3771 4.4 14.3771H12.2545C12.3692 14.3771 12.4826 14.3545 12.5885 14.3106C12.6944 14.2668 12.7906 14.2025 12.8717 14.1214C12.9527 14.0404 13.017 13.9442 13.0608 13.8383C13.1047 13.7324 13.1273 13.6189 13.1273 13.5043V10.0418C13.1273 9.60717 13.4244 9.20397 13.8534 9.13415C13.9786 9.11304 14.1069 9.11947 14.2294 9.153C14.3519 9.18652 14.4656 9.24634 14.5627 9.32829C14.6597 9.41024 14.7377 9.51234 14.7912 9.6275C14.8448 9.74266 14.8726 9.8681 14.8727 9.9951V13.5043C14.8727 13.6189 14.8953 13.7324 14.9392 13.8383C14.983 13.9442 15.0473 14.0404 15.1283 14.1214C15.2094 14.2025 15.3056 14.2668 15.4115 14.3106C15.5174 14.3545 15.6308 14.3771 15.7455 14.3771H23.6C24.0629 14.3771 24.5069 14.1932 24.8342 13.8658C25.1616 13.5385 25.3455 13.0945 25.3455 12.6316V9.1407C25.3455 8.67778 25.1616 8.23381 24.8342 7.90648C24.5069 7.57914 24.0629 7.39525 23.6 7.39525ZM9.4256 7.39525L8.11476 6.78041C7.83665 6.65214 7.59404 6.45796 7.40797 6.2147C7.22189 5.97145 7.09798 5.68647 7.04698 5.38448C6.99231 5.08313 7.01194 4.77298 7.10417 4.48093C7.1964 4.18887 7.35845 3.92371 7.57629 3.70841C7.7913 3.49017 8.05644 3.32784 8.34858 3.23558C8.64073 3.14332 8.951 3.12393 9.25236 3.1791C9.55433 3.22999 9.83931 3.35381 10.0826 3.53981C10.3258 3.72581 10.52 3.96837 10.6483 4.24644L12.1254 7.39525H9.4256ZM20.9526 5.38448C20.9018 5.68647 20.778 5.97149 20.592 6.21477C20.406 6.45804 20.1633 6.65221 19.8852 6.78041L18.5744 7.39525H15.8742L17.3513 4.24644C17.4795 3.96828 17.6737 3.72564 17.9171 3.53962C18.1605 3.35361 18.4456 3.22985 18.7476 3.1791C19.0489 3.12408 19.3591 3.14355 19.6511 3.2358C19.9432 3.32805 20.2083 3.4903 20.4233 3.70841C20.6412 3.92362 20.8034 4.18877 20.8956 4.48086C20.9878 4.77294 21.0074 5.08313 20.9526 5.38448Z" fill="#DEDEDE"/>
</g>
</g>
<defs>
<clipPath id="clip0_64_190">
<rect width="28" height="28" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,12 +0,0 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_144_244)">
<g id="Frame_2">
<path id="Vector" d="M14.8728 16.9952V25.7225C14.8728 25.8371 14.8953 25.9506 14.9392 26.0565C14.9831 26.1624 15.0473 26.2586 15.1284 26.3396C15.2094 26.4207 15.3056 26.485 15.4115 26.5288C15.5174 26.5727 15.6309 26.5952 15.7455 26.5952H21.8546C22.3175 26.5952 22.7615 26.4114 23.0888 26.084C23.4161 25.7567 23.6 25.3127 23.6 24.8498V16.9952C23.6 16.8806 23.5775 16.7672 23.5336 16.6613C23.4898 16.5554 23.4255 16.4592 23.3444 16.3781C23.2634 16.2971 23.1672 16.2328 23.0613 16.189C22.9554 16.1451 22.8419 16.1225 22.7273 16.1225H15.7455C15.6309 16.1225 15.5174 16.1451 15.4115 16.189C15.3056 16.2328 15.2094 16.2971 15.1284 16.3781C15.0473 16.4592 14.9831 16.5554 14.9392 16.6613C14.8953 16.7672 14.8728 16.8806 14.8728 16.9952ZM12.2546 16.1225H5.27277C5.15816 16.1225 5.04467 16.1451 4.93879 16.189C4.83291 16.2328 4.7367 16.2971 4.65566 16.3781C4.57462 16.4592 4.51033 16.5554 4.46647 16.6613C4.42262 16.7672 4.40004 16.8806 4.40004 16.9952V24.8498C4.40004 25.3127 4.58394 25.7567 4.91127 26.084C5.23861 26.4114 5.68257 26.5952 6.1455 26.5952H12.2546C12.3692 26.5952 12.4827 26.5727 12.5886 26.5288C12.6945 26.485 12.7907 26.4207 12.8717 26.3396C12.9527 26.2586 13.017 26.1624 13.0609 26.0565C13.1047 25.9506 13.1273 25.8371 13.1273 25.7225V16.9952C13.1273 16.8806 13.1047 16.7672 13.0609 16.6613C13.017 16.5554 12.9527 16.4592 12.8717 16.3781C12.7907 16.2971 12.6945 16.2328 12.5886 16.189C12.4827 16.1451 12.3692 16.1225 12.2546 16.1225ZM23.6 7.39525H21.8699C22.2828 6.90556 22.5594 6.31573 22.6719 5.68514C22.7728 5.10782 22.7333 4.5147 22.5567 3.95586C22.3801 3.39703 22.0717 2.88889 21.6574 2.47437C21.1026 1.92075 20.3847 1.55987 19.6095 1.44487C18.8342 1.32986 18.0425 1.4668 17.3509 1.83554C16.6265 2.21823 16.0728 2.86099 15.725 3.60281L14 7.28048L12.2607 3.57314C11.9714 2.95612 11.539 2.4063 10.9712 2.02972C9.44615 1.01823 7.5327 1.28397 6.34273 2.47437C5.92849 2.88893 5.62006 3.39707 5.44348 3.95589C5.2669 4.51472 5.22736 5.10782 5.32819 5.68514C5.44066 6.31573 5.71729 6.90556 6.13022 7.39525H4.40004C3.93712 7.39525 3.49315 7.57914 3.16582 7.90648C2.83848 8.23381 2.65459 8.67778 2.65459 9.1407V12.6316C2.65459 13.0945 2.83848 13.5385 3.16582 13.8658C3.49315 14.1932 3.93712 14.3771 4.40004 14.3771H12.2546C12.3692 14.3771 12.4827 14.3545 12.5886 14.3106C12.6945 14.2668 12.7907 14.2025 12.8717 14.1214C12.9527 14.0404 13.017 13.9442 13.0609 13.8383C13.1047 13.7324 13.1273 13.6189 13.1273 13.5043V10.0418C13.1273 9.60717 13.4245 9.20397 13.8534 9.13415C13.9787 9.11304 14.107 9.11947 14.2295 9.153C14.352 9.18652 14.4657 9.24634 14.5627 9.32829C14.6597 9.41024 14.7377 9.51234 14.7913 9.6275C14.8448 9.74266 14.8726 9.8681 14.8728 9.9951V13.5043C14.8728 13.6189 14.8953 13.7324 14.9392 13.8383C14.9831 13.9442 15.0473 14.0404 15.1284 14.1214C15.2094 14.2025 15.3056 14.2668 15.4115 14.3106C15.5174 14.3545 15.6309 14.3771 15.7455 14.3771H23.6C24.063 14.3771 24.5069 14.1932 24.8343 13.8658C25.1616 13.5385 25.3455 13.0945 25.3455 12.6316V9.1407C25.3455 8.67778 25.1616 8.23381 24.8343 7.90648C24.5069 7.57914 24.063 7.39525 23.6 7.39525ZM9.42564 7.39525L8.11481 6.78041C7.8367 6.65214 7.59408 6.45796 7.40801 6.2147C7.22194 5.97145 7.09803 5.68647 7.04702 5.38448C6.99235 5.08313 7.01198 4.77298 7.10421 4.48093C7.19645 4.18887 7.3585 3.92371 7.57633 3.70841C7.79135 3.49017 8.05648 3.32784 8.34863 3.23558C8.64077 3.14332 8.95105 3.12393 9.25241 3.1791C9.55437 3.22999 9.83936 3.35381 10.0826 3.53981C10.3259 3.72581 10.5201 3.96837 10.6483 4.24644L12.1254 7.39525H9.42564ZM20.9526 5.38448C20.9018 5.68647 20.778 5.97149 20.592 6.21477C20.406 6.45804 20.1634 6.65221 19.8853 6.78041L18.5744 7.39525H15.8742L17.3513 4.24644C17.4796 3.96828 17.6738 3.72564 17.9171 3.53962C18.1605 3.35361 18.4456 3.22985 18.7477 3.1791C19.049 3.12408 19.3591 3.14355 19.6512 3.2358C19.9432 3.32805 20.2083 3.4903 20.4233 3.70841C20.6413 3.92362 20.8034 4.18877 20.8957 4.48086C20.9879 4.77294 21.0074 5.08313 20.9526 5.38448Z" fill="#1797FF"/>
</g>
</g>
<defs>
<clipPath id="clip0_144_244">
<rect width="28" height="28" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,10 +0,0 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_64_205)">
<path id="Vector" d="M25.9103 12.7915C25.8163 12.2565 25.3128 11.7119 24.7905 11.5914L24.4001 11.4998C23.4826 11.2154 22.6686 10.5936 22.1511 9.67793C21.6335 8.75736 21.5111 7.71144 21.737 6.75244L21.8594 6.37645C22.0146 5.85592 21.8123 5.13783 21.403 4.78112C21.403 4.78112 21.036 4.46304 20.0009 3.85093C18.9658 3.24365 18.5188 3.07498 18.5188 3.07498C18.0153 2.88704 17.3096 3.06536 16.9426 3.4654L16.6697 3.76422C15.9734 4.43898 15.0371 4.84864 14.002 4.84864C12.967 4.84864 12.0213 4.43422 11.3249 3.7546L11.0614 3.46537C10.6991 3.06536 9.98869 2.8871 9.48519 3.07498C9.48519 3.07498 9.03354 3.24365 7.99842 3.8509C6.96331 4.46783 6.60105 4.78591 6.60105 4.78591C6.19176 5.13777 5.98942 5.85106 6.14469 6.37642L6.2577 6.75716C6.47882 7.71623 6.36116 8.75736 5.8436 9.67783C5.32604 10.5983 4.50291 11.2251 3.58084 11.5046L3.20445 11.5914C2.68689 11.7119 2.17872 12.2517 2.08465 12.7915C2.08465 12.7915 2 13.2734 2 14.4976C2 15.7218 2.08465 16.2038 2.08465 16.2038C2.17872 16.7436 2.68218 17.2834 3.20445 17.4039L3.57148 17.4906C4.49362 17.7702 5.32169 18.3968 5.83925 19.3221C6.35681 20.2427 6.47921 21.2886 6.25335 22.2476L6.14518 22.6187C5.98994 23.1393 6.19228 23.8574 6.60151 24.2141C6.60151 24.2141 6.96854 24.5322 8.00362 25.1443C9.03871 25.7564 9.48568 25.9202 9.48568 25.9202C9.98914 26.1081 10.6948 25.9298 11.0618 25.5298L11.3206 25.2454C12.0216 24.5658 12.9626 24.1514 14.0024 24.1514C15.0423 24.1514 15.9879 24.5706 16.6843 25.2502L16.943 25.5346C17.3053 25.9346 18.0157 26.113 18.5192 25.925C18.5192 25.925 18.9708 25.7563 20.006 25.1491C21.0411 24.5369 21.4033 24.2189 21.4033 24.2189C21.8126 23.867 22.015 23.1489 21.8597 22.6235L21.7467 22.2379C21.5255 21.2837 21.6432 20.2426 22.1608 19.3268C22.6783 18.4062 23.5063 17.7749 24.4285 17.4954L24.7955 17.4086C25.3131 17.2881 25.8213 16.7483 25.9153 16.2085C25.9153 16.2085 26 15.7266 26 14.5024C25.9951 13.2733 25.9104 12.7913 25.9104 12.7913L25.9103 12.7915ZM14.0023 19.3992C11.3629 19.3992 9.21743 17.2063 9.21743 14.4976C9.21743 11.7938 11.3581 9.6008 14.0023 9.6008C16.6418 9.6008 18.7872 11.7937 18.7872 14.5024C18.7824 17.2063 16.6417 19.3992 14.0023 19.3992Z" fill="#DEDEDE"/>
</g>
<defs>
<clipPath id="clip0_64_205">
<rect width="28" height="28" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,10 +0,0 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_144_284)">
<path id="Vector" d="M25.9103 12.7915C25.8163 12.2565 25.3128 11.7119 24.7905 11.5914L24.4001 11.4998C23.4826 11.2154 22.6686 10.5936 22.1511 9.67793C21.6335 8.75736 21.5111 7.71144 21.737 6.75244L21.8594 6.37645C22.0146 5.85592 21.8123 5.13783 21.403 4.78112C21.403 4.78112 21.036 4.46304 20.0009 3.85093C18.9658 3.24365 18.5188 3.07498 18.5188 3.07498C18.0153 2.88704 17.3096 3.06536 16.9426 3.4654L16.6697 3.76422C15.9734 4.43898 15.0371 4.84864 14.002 4.84864C12.967 4.84864 12.0213 4.43422 11.3249 3.7546L11.0614 3.46537C10.6991 3.06536 9.98869 2.8871 9.48519 3.07498C9.48519 3.07498 9.03354 3.24365 7.99842 3.8509C6.96331 4.46783 6.60105 4.78591 6.60105 4.78591C6.19176 5.13777 5.98942 5.85106 6.14469 6.37642L6.2577 6.75716C6.47882 7.71623 6.36116 8.75736 5.8436 9.67783C5.32604 10.5983 4.50291 11.2251 3.58084 11.5046L3.20445 11.5914C2.68689 11.7119 2.17872 12.2517 2.08465 12.7915C2.08465 12.7915 2 13.2734 2 14.4976C2 15.7218 2.08465 16.2038 2.08465 16.2038C2.17872 16.7436 2.68218 17.2834 3.20445 17.4039L3.57148 17.4906C4.49362 17.7702 5.32169 18.3968 5.83925 19.3221C6.35681 20.2427 6.47921 21.2886 6.25335 22.2476L6.14518 22.6187C5.98994 23.1393 6.19228 23.8574 6.60151 24.2141C6.60151 24.2141 6.96854 24.5322 8.00362 25.1443C9.03871 25.7564 9.48568 25.9202 9.48568 25.9202C9.98914 26.1081 10.6948 25.9298 11.0618 25.5298L11.3206 25.2454C12.0216 24.5658 12.9626 24.1514 14.0024 24.1514C15.0423 24.1514 15.9879 24.5706 16.6843 25.2502L16.943 25.5346C17.3053 25.9346 18.0157 26.113 18.5192 25.925C18.5192 25.925 18.9708 25.7563 20.006 25.1491C21.0411 24.5369 21.4033 24.2189 21.4033 24.2189C21.8126 23.867 22.015 23.1489 21.8597 22.6235L21.7467 22.2379C21.5255 21.2837 21.6432 20.2426 22.1608 19.3268C22.6783 18.4062 23.5063 17.7749 24.4285 17.4954L24.7955 17.4086C25.3131 17.2881 25.8213 16.7483 25.9153 16.2085C25.9153 16.2085 26 15.7266 26 14.5024C25.9951 13.2733 25.9104 12.7913 25.9104 12.7913L25.9103 12.7915ZM14.0023 19.3992C11.3629 19.3992 9.21743 17.2063 9.21743 14.4976C9.21743 11.7938 11.3581 9.6008 14.0023 9.6008C16.6418 9.6008 18.7872 11.7937 18.7872 14.5024C18.7824 17.2063 16.6417 19.3992 14.0023 19.3992Z" fill="#1797FF"/>
</g>
<defs>
<clipPath id="clip0_144_284">
<rect width="28" height="28" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,11 +0,0 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_64_203)">
<path id="Vector" d="M22.4125 14.9461C23.3523 14.9461 24.1271 15.7118 23.9826 16.6408C23.583 19.208 22.2927 21.5527 20.3378 23.2635C18.3829 24.9744 15.8883 25.9423 13.2914 25.9975C10.6944 26.0527 8.16092 25.1917 6.13514 23.5655C4.10935 21.9392 2.72058 19.6515 2.21235 17.1036C1.92144 15.6403 1.92951 14.1334 2.23606 12.6733C2.54261 11.2133 3.14131 9.8304 3.99614 8.60786C4.85096 7.38531 5.94423 6.34839 7.21015 5.5595C8.47608 4.77062 9.88848 4.24607 11.3624 4.01743C12.2903 3.8728 13.0566 4.64868 13.0566 5.5879V13.2446C13.0566 13.6959 13.2359 14.1286 13.5549 14.4477C13.8739 14.7668 14.3065 14.9461 14.7577 14.9461H22.4125Z" fill="#DEDEDE"/>
<path id="Vector_2" d="M24.4043 13C25.349 13 26.1278 12.2306 25.9825 11.297C25.6206 8.96568 24.5264 6.80997 22.8582 5.14175C21.19 3.47354 19.0343 2.37935 16.703 2.01751C15.7703 1.87218 15 2.65188 15 3.59572V11.2901C15 11.7436 15.1801 12.1785 15.5008 12.4992C15.8215 12.8199 16.2564 13 16.7099 13H24.4043Z" fill="#DEDEDE"/>
</g>
<defs>
<clipPath id="clip0_64_203">
<rect width="28" height="28" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,12 +0,0 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_144_265)">
<path id="Vector" opacity="0.01" d="M2 2H26V26H2V2Z" fill="#1797FF"/>
<path id="Vector_2" d="M22.4125 14.9461C23.3523 14.9461 24.1271 15.7118 23.9826 16.6408C23.583 19.208 22.2927 21.5527 20.3378 23.2635C18.3829 24.9744 15.8883 25.9423 13.2914 25.9975C10.6944 26.0527 8.16092 25.1917 6.13514 23.5655C4.10935 21.9392 2.72058 19.6515 2.21235 17.1036C1.92144 15.6403 1.92951 14.1334 2.23606 12.6733C2.54261 11.2133 3.14131 9.8304 3.99614 8.60786C4.85096 7.38531 5.94423 6.34839 7.21015 5.5595C8.47608 4.77062 9.88848 4.24607 11.3624 4.01743C12.2903 3.8728 13.0566 4.64868 13.0566 5.5879V13.2446C13.0566 13.6959 13.2359 14.1286 13.5549 14.4477C13.8739 14.7668 14.3065 14.9461 14.7577 14.9461H22.4125Z" fill="#1797FF"/>
<path id="Vector_3" d="M24.4043 13C25.349 13 26.1278 12.2306 25.9825 11.297C25.6206 8.96568 24.5264 6.80997 22.8582 5.14175C21.19 3.47354 19.0343 2.37935 16.703 2.01751C15.7703 1.87218 15 2.65188 15 3.59572V11.2901C15 11.7436 15.1801 12.1785 15.5008 12.4992C15.8215 12.8199 16.2564 13 16.7099 13H24.4043Z" fill="#1797FF"/>
</g>
<defs>
<clipPath id="clip0_144_265">
<rect width="28" height="28" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,3 +1,4 @@
description: This file stores settings for Dart & Flutter DevTools. description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions: extensions:
- drift: true

View File

@ -3,6 +3,7 @@ import '../services/api_service/kr_api.user.dart';
import '../utils/kr_update_util.dart'; import '../utils/kr_update_util.dart';
import '../utils/kr_secure_storage.dart'; import '../utils/kr_secure_storage.dart';
import '../utils/kr_log_util.dart'; import '../utils/kr_log_util.dart';
import '../utils/kr_http_adapter_util.dart';
import '../services/singbox_imp/kr_sing_box_imp.dart'; import '../services/singbox_imp/kr_sing_box_imp.dart';
import '../utils/kr_init_log_collector.dart'; // 🔧 import '../utils/kr_init_log_collector.dart'; // 🔧
import 'dart:async'; import 'dart:async';
@ -31,7 +32,7 @@ class KRDomain {
// static String kr_currentDomain = "apicn.bearvpn.top"; // static String kr_currentDomain = "apicn.bearvpn.top";
static List<String> kr_baseDomains = ["api.hifast.biz", "api.airovpn.tel",]; static List<String> kr_baseDomains = ["api.hifast.biz", "api.airovpn.tel",];
static String kr_currentDomain = "api.hifast.biz1"; static String kr_currentDomain = "api.hifast.biz";
// //
static List<String> kr_backupDomainUrls = [ static List<String> kr_backupDomainUrls = [
@ -67,21 +68,14 @@ class KRDomain {
// Dio // Dio
static final Dio _dio = (() { static final Dio _dio = (() {
final dio = Dio(); final dio = Dio(BaseOptions(
// 🔧 HttpClientAdapter使用sing-box的mixed代理 connectTimeout: const Duration(seconds: kr_domainTimeout),
dio.httpClientAdapter = IOHttpClientAdapter( sendTimeout: const Duration(seconds: kr_domainTimeout),
createHttpClient: () { receiveTimeout: const Duration(seconds: kr_domainTimeout),
final client = HttpClient(); ));
client.findProxy = (url) { // 🔧 使 Adapter
final proxyConfig = KRSingBoxImp.instance.kr_buildProxyRule(); dio.httpClientAdapter = KRHttpAdapterUtil.createAdapter(
KRLogUtil.kr_i( timeout: const Duration(seconds: kr_domainTimeout),
'🔍 KRDomain 请求使用代理: $proxyConfig, url: $url',
tag: 'KRDomain',
);
return proxyConfig;
};
return client;
},
); );
return dio; return dio;
})(); })();

View File

@ -44,6 +44,9 @@ class KRAppRunData {
/// ///
final RxString kr_referCode = ''.obs; final RxString kr_referCode = ''.obs;
/// ID
final RxInt kr_refererId = 0.obs;
/// ///
final RxInt kr_balance = 0.obs; final RxInt kr_balance = 0.obs;
@ -597,12 +600,14 @@ class KRAppRunData {
// //
kr_referCode.value = userInfo.referCode; kr_referCode.value = userInfo.referCode;
kr_refererId.value = userInfo.refererId;
kr_account.value = authType == 'device' ? '9000${userInfo.id}' : authIdentifier; kr_account.value = authType == 'device' ? '9000${userInfo.id}' : authIdentifier;
kr_balance.value = userInfo.balance; kr_balance.value = userInfo.balance;
kr_commission.value = userInfo.commission; kr_commission.value = userInfo.commission;
KRLogUtil.kr_i('💾 [AppRunData] 用户信息已保存到全局状态:', tag: 'AppRunData'); KRLogUtil.kr_i('💾 [AppRunData] 用户信息已保存到全局状态:', tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_referCode: "${kr_referCode.value}"', tag: 'AppRunData'); KRLogUtil.kr_i(' - kr_referCode: "${kr_referCode.value}"', tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_refererId: ${kr_refererId.value}', tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_balance: ${kr_balance.value}', tag: 'AppRunData'); KRLogUtil.kr_i(' - kr_balance: ${kr_balance.value}', tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_commission: ${kr_commission.value}', tag: 'AppRunData'); KRLogUtil.kr_i(' - kr_commission: ${kr_commission.value}', tag: 'AppRunData');
}, },

View File

@ -1,12 +0,0 @@
import 'package:get/get.dart';
import '../controllers/kr_country_selector_controller.dart';
class KRCountrySelectorBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<KRCountrySelectorController>(
() => KRCountrySelectorController(),
);
}
}

View File

@ -1,43 +0,0 @@
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/common/app_config.dart';
import 'package:kaer_with_panels/app/utils/kr_country_util.dart';
import '../../../services/singbox_imp/kr_sing_box_imp.dart';
class KRCountrySelectorController extends GetxController {
// 使 KRCountry
final RxList<KRCountry> kr_countries = <KRCountry>[].obs;
//
final Rx<KRCountry> kr_selectedCountry = KRCountry.cn.obs;
@override
void onInit() {
super.onInit();
kr_selectedCountry.value = KRCountryUtil.kr_currentCountry.value;
kr_loadCountries();
}
//
void kr_loadCountries() {
kr_countries.value = KRCountryUtil.kr_getSupportedCountries();
}
//
Future<void> kr_selectCountry(KRCountry country) async {
kr_selectedCountry.value = country;
// try {
// await KRSingBoxImp().kr_updateCountry(country);
// // Get.back();
// } catch (err) {
// }
}
@override
void onClose() {
// TODO: implement onClose
super.onClose();
KRSingBoxImp().kr_updateCountry(kr_selectedCountry.value);
}
}

View File

@ -1,116 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/utils/kr_country_util.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import 'package:kaer_with_panels/app/widgets/kr_country_flag.dart';
import '../controllers/kr_country_selector_controller.dart';
class KRCountrySelectorView extends GetView<KRCountrySelectorController> {
const KRCountrySelectorView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: Theme.of(context).primaryColor,
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color.fromRGBO(23, 151, 255, 0.15), //
Color.fromRGBO(23, 151, 255, 0.05), //
//
],
stops: [0.0, 0.28], //
),
),
child: Column(
children: [
AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 20.r,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
onPressed: () => Get.back(),
),
title: Text(
AppTranslations.kr_setting.countrySelector,
style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
centerTitle: true,
),
Expanded(
child: Obx(
() => ListView.separated(
padding: EdgeInsets.all(16.r),
itemCount: controller.kr_countries.length,
separatorBuilder: (context, index) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
final country = controller.kr_countries[index];
return _kr_buildCountryCard(country, context);
},
),
),
),
],
),
),
);
}
//
Widget _kr_buildCountryCard(KRCountry country, BuildContext context) {
return Obx(
() => InkWell(
onTap: () => controller.kr_selectCountry(country),
child: Container(
padding: EdgeInsets.all(16.r),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12.r),
),
child: Row(
children: [
//
KRCountryFlag(
countryCode: country.kr_code,
width: 24.r,
height: 24.r,
),
SizedBox(width: 12.w),
//
Text(
KRCountryUtil.kr_getCountryName(country),
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
const Spacer(),
//
if (controller.kr_selectedCountry.value == country)
Icon(
Icons.check_circle,
color: Colors.blue,
size: 20.r,
),
],
),
),
),
);
}
}

View File

@ -1,12 +0,0 @@
import 'package:get/get.dart';
import '../controllers/kr_device_management_controller.dart';
class KRDeviceManagementBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<KRDeviceManagementController>(
() => KRDeviceManagementController(),
);
}
}

View File

@ -1,294 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import 'package:kaer_with_panels/app/services/api_service/kr_api.user.dart';
import 'package:kaer_with_panels/app/widgets/dialogs/kr_dialog.dart';
import 'package:kaer_with_panels/app/common/app_run_data.dart';
import 'package:kaer_with_panels/app/services/api_service/kr_auth_api.dart';
import 'package:kaer_with_panels/app/services/kr_site_config_service.dart';
import 'package:kaer_with_panels/app/services/kr_device_info_service.dart';
import 'package:kaer_with_panels/app/services/kr_subscribe_service.dart';
import 'package:kaer_with_panels/app/model/enum/kr_request_type.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'dart:io';
import 'dart:math';
class KRDeviceManagementController extends GetxController {
//
final RxList<Map<String, dynamic>> devices = <Map<String, dynamic>>[].obs;
//
final RxBool isLoading = true.obs;
// ID
String? currentDeviceId;
@override
void onInit() {
super.onInit();
_initDeviceId();
loadDeviceList();
}
/// ID
Future<void> _initDeviceId() async {
try {
currentDeviceId = KRDeviceInfoService().deviceId ?? 'unknown';
KRLogUtil.kr_i('当前设备ID: $currentDeviceId', tag: 'DeviceManagement');
} catch (e) {
KRLogUtil.kr_e('获取设备ID失败: $e', tag: 'DeviceManagement');
}
}
///
Future<void> loadDeviceList() async {
try {
isLoading.value = true;
KRLogUtil.kr_i('开始加载设备列表', tag: 'DeviceManagement');
// API获取设备列表
final result = await KRUserApi().kr_getUserDevices();
result.fold(
(error) {
KRLogUtil.kr_e('加载设备列表失败: ${error.msg}', tag: 'DeviceManagement');
Get.snackbar(AppTranslations.kr_dialog.error, error.msg);
},
(deviceList) {
KRLogUtil.kr_i('获取到 ${deviceList.length} 个设备', tag: 'DeviceManagement');
//
devices.value = deviceList.map((device) {
final identifier = device['identifier']?.toString() ?? '';
final isCurrent = identifier == currentDeviceId;
return {
'id': device['id']?.toString() ?? '',
'identifier': identifier,
'device_name': device['user_agent'] ?? '未知设备',
'ip': device['ip'] ?? '',
'last_login': device['updated_at'] ?? device['created_at'] ?? '',
'is_current': isCurrent,
'enabled': device['enabled'] ?? true,
'online': device['online'] ?? false,
};
}).toList();
},
);
} catch (e, stackTrace) {
KRLogUtil.kr_e('加载设备列表异常: $e', tag: 'DeviceManagement');
KRLogUtil.kr_e('堆栈跟踪: $stackTrace', tag: 'DeviceManagement');
Get.snackbar(AppTranslations.kr_dialog.error, AppTranslations.kr_deviceManagement.loadDeviceListFailed);
} finally {
isLoading.value = false;
}
}
///
Future<void> deleteDevice(String id) async {
try {
//
final device = devices.firstWhere(
(d) => d['id'] == id,
orElse: () => {},
);
if (device.isEmpty) return;
final isCurrent = device['is_current'] ?? false;
// 使
bool? confirmed;
//
await KRDialog.show(
title: AppTranslations.kr_deviceManagement.deleteConfirmTitle,
message: isCurrent
? AppTranslations.kr_deviceManagement.deleteCurrentDeviceMessage
: AppTranslations.kr_deviceManagement.deleteOtherDeviceMessage,
icon: Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(
Icons.warning_rounded,
color: Colors.red,
size: 32,
),
),
confirmText: AppTranslations.kr_dialog.delete,
cancelText: AppTranslations.kr_dialog.kr_cancel,
onConfirm: () {
confirmed = true;
},
onCancel: () {
confirmed = false;
},
);
if (confirmed != true) return;
KRLogUtil.kr_i('开始解绑设备 - id: $id, isCurrent: $isCurrent', tag: 'DeviceManagement');
// API解绑设备
final result = await KRUserApi().kr_unbindUserDevice(id);
result.fold(
(error) {
KRLogUtil.kr_e('删除设备失败: ${error.msg}', tag: 'DeviceManagement');
Get.snackbar(AppTranslations.kr_dialog.error, AppTranslations.kr_deviceManagement.deleteFailed(error.msg));
},
(_) async {
KRLogUtil.kr_i('设备删除成功', tag: 'DeviceManagement');
if (isCurrent) {
//
KRLogUtil.kr_i('本机设备已删除,准备重新登录', tag: 'DeviceManagement');
//
Get.back();
//
await _reloginWithDevice();
} else {
//
devices.removeWhere((device) => device['id'] == id);
Get.snackbar(AppTranslations.kr_dialog.success, AppTranslations.kr_deviceManagement.deleteSuccess);
}
},
);
} catch (e, stackTrace) {
KRLogUtil.kr_e('删除设备异常: $e', tag: 'DeviceManagement');
KRLogUtil.kr_e('堆栈跟踪: $stackTrace', tag: 'DeviceManagement');
Get.snackbar(AppTranslations.kr_dialog.error, AppTranslations.kr_deviceManagement.deleteFailed(e.toString()));
}
}
/// 使
Future<void> _reloginWithDevice() async {
try {
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'DeviceManagement');
KRLogUtil.kr_i('开始重新进行设备登录', tag: 'DeviceManagement');
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'DeviceManagement');
// kr_loginOut
final appRunData = KRAppRunData.getInstance();
appRunData.kr_isLogin.value = false;
appRunData.kr_token = null;
appRunData.kr_account.value = null;
appRunData.kr_userId.value = null;
//
final siteConfigService = KRSiteConfigService();
final isDeviceLoginEnabled = siteConfigService.isDeviceLoginEnabled();
if (!isDeviceLoginEnabled) {
KRLogUtil.kr_w('设备登录未启用,执行完整退出登录', tag: 'DeviceManagement');
Get.snackbar(AppTranslations.kr_dialog.tip, AppTranslations.kr_deviceManagement.deviceLoginDisabled);
await appRunData.kr_loginOut();
return;
}
KRLogUtil.kr_i('设备登录已启用,开始调用设备登录接口', tag: 'DeviceManagement');
//
await KRDeviceInfoService().initialize();
//
final authApi = KRAuthApi();
final result = await authApi.kr_deviceLogin();
result.fold(
(error) {
//
KRLogUtil.kr_e('设备登录失败: ${error.msg}', tag: 'DeviceManagement');
Get.snackbar(AppTranslations.kr_dialog.error, AppTranslations.kr_deviceManagement.reloginFailed(error.msg));
// 退
appRunData.kr_loginOut();
},
(token) async {
//
KRLogUtil.kr_i('✅ 设备登录成功!', tag: 'DeviceManagement');
KRLogUtil.kr_i('🎫 Token: ${token.substring(0, min(20, token.length))}...', tag: 'DeviceManagement');
//
final deviceId = KRDeviceInfoService().deviceId ?? 'unknown';
await appRunData.kr_saveUserInfo(
token,
'device_$deviceId',
KRLoginType.kr_email,
null,
);
KRLogUtil.kr_i('✅ 设备重新登录成功,已更新用户信息', tag: 'DeviceManagement');
//
await Future.delayed(const Duration(milliseconds: 300));
//
KRLogUtil.kr_i('🔄 开始刷新订阅信息...', tag: 'DeviceManagement');
try {
await KRSubscribeService().kr_refreshAll();
KRLogUtil.kr_i('✅ 订阅信息刷新成功', tag: 'DeviceManagement');
} catch (e) {
KRLogUtil.kr_e('订阅信息刷新失败: $e', tag: 'DeviceManagement');
}
Get.snackbar(AppTranslations.kr_dialog.success, AppTranslations.kr_deviceManagement.reloginSuccess);
},
);
} catch (e, stackTrace) {
KRLogUtil.kr_e('设备重新登录异常: $e', tag: 'DeviceManagement');
KRLogUtil.kr_e('堆栈跟踪: $stackTrace', tag: 'DeviceManagement');
Get.snackbar(AppTranslations.kr_dialog.error, AppTranslations.kr_deviceManagement.reloginFailedGeneric);
// 退
await KRAppRunData.getInstance().kr_loginOut();
}
}
///
Map<String, dynamic> getDeviceTypeInfo(String userAgent) {
String deviceType = AppTranslations.kr_deviceManagement.deviceTypeUnknown;
String iconName = 'devices';
if (userAgent.contains('Android') || userAgent.toLowerCase().contains('android')) {
deviceType = AppTranslations.kr_deviceManagement.deviceTypeAndroid;
iconName = 'phone_android';
} else if (userAgent.contains('iOS') || userAgent.contains('iPhone') || userAgent.toLowerCase().contains('ios')) {
deviceType = AppTranslations.kr_deviceManagement.deviceTypeIos;
iconName = 'phone_iphone';
} else if (userAgent.contains('iPad')) {
deviceType = AppTranslations.kr_deviceManagement.deviceTypeIpad;
iconName = 'tablet';
} else if (userAgent.contains('macOS') || userAgent.contains('Mac') || userAgent.toLowerCase().contains('mac')) {
deviceType = AppTranslations.kr_deviceManagement.deviceTypeMacos;
iconName = 'desktop_mac';
} else if (userAgent.contains('Windows') || userAgent.toLowerCase().contains('windows')) {
deviceType = AppTranslations.kr_deviceManagement.deviceTypeWindows;
iconName = 'computer';
} else if (userAgent.contains('Linux') || userAgent.toLowerCase().contains('linux')) {
deviceType = AppTranslations.kr_deviceManagement.deviceTypeLinux;
iconName = 'computer';
}
return {
'type': deviceType,
'icon': iconName,
};
}
@override
void onReady() {
super.onReady();
}
@override
void onClose() {
super.onClose();
}
}

View File

@ -1,313 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import '../controllers/kr_device_management_controller.dart';
class KRDeviceManagementView extends GetView<KRDeviceManagementController> {
const KRDeviceManagementView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: Theme.of(context).primaryColor,
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color.fromRGBO(23, 151, 255, 0.15),
Color.fromRGBO(23, 151, 255, 0.05),
],
stops: [0.0, 0.28],
),
),
child: Column(
children: [
//
AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
color: Theme.of(context).iconTheme.color,
size: 20.w,
),
onPressed: () => Get.back(),
),
title: Text(
'设备管理',
style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
//
Expanded(
child: Obx(() {
if (controller.isLoading.value) {
return Center(
child: CircularProgressIndicator(),
);
}
if (controller.devices.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.devices_other,
size: 64.w,
color: Theme.of(context).textTheme.bodySmall?.color,
),
SizedBox(height: 16.h),
Text(
'暂无登录设备',
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
],
),
);
}
return RefreshIndicator(
onRefresh: () => controller.loadDeviceList(),
child: ListView.builder(
padding: EdgeInsets.all(16.w),
itemCount: controller.devices.length,
itemBuilder: (context, index) {
return _buildDeviceItem(
context,
controller.devices[index],
);
},
),
);
}),
),
],
),
),
);
}
///
Widget _buildDeviceItem(
BuildContext context, Map<String, dynamic> device) {
final id = device['id'] ?? '';
final identifier = device['identifier'] ?? '';
final userAgent = device['device_name'] ?? '未知设备';
final isCurrent = device['is_current'] ?? false;
final ip = device['ip'] ?? '';
final lastLoginRaw = device['last_login'];
final String lastLogin = lastLoginRaw?.toString() ?? '';
//
final deviceInfo = controller.getDeviceTypeInfo(userAgent);
final deviceType = deviceInfo['type'] as String;
final iconName = deviceInfo['icon'] as String;
return Container(
margin: EdgeInsets.only(bottom: 12.w),
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12.w),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 10.w,
offset: Offset(0, 2.w),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Row(
children: [
//
Container(
width: 48.w,
height: 48.w,
decoration: BoxDecoration(
color: const Color(0xFF1797FF).withOpacity(0.1),
borderRadius: BorderRadius.circular(8.w),
),
child: Icon(
_getIconData(iconName),
color: const Color(0xFF1797FF),
size: 24.w,
),
),
SizedBox(width: 12.w),
//
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
deviceType,
style: KrAppTextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
if (isCurrent) ...[
SizedBox(width: 8.w),
Container(
padding: EdgeInsets.symmetric(
horizontal: 8.w,
vertical: 2.h,
),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(4.w),
),
child: Text(
'本机',
style: KrAppTextStyle(
fontSize: 10,
color: Colors.green,
),
),
),
],
],
),
SizedBox(height: 4.h),
Text(
'ID: ${identifier.substring(0, identifier.length > 12 ? 12 : identifier.length)}...',
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
],
),
),
//
TextButton(
onPressed: () => controller.deleteDevice(id),
style: TextButton.styleFrom(
foregroundColor: Theme.of(context).colorScheme.error,
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
),
child: Text(
'删除',
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.error,
),
),
),
],
),
// 线
if (ip.isNotEmpty || lastLogin.isNotEmpty) ...[
SizedBox(height: 12.h),
Divider(height: 1, color: Theme.of(context).dividerColor),
SizedBox(height: 12.h),
],
//
if (ip.isNotEmpty)
_buildInfoRow(
context,
'IP地址',
ip,
),
if (ip.isNotEmpty && lastLogin.isNotEmpty) SizedBox(height: 8.h),
if (lastLogin.isNotEmpty)
_buildInfoRow(
context,
'最后登录',
_formatDateTime(lastLoginRaw),
),
],
),
);
}
///
Widget _buildInfoRow(BuildContext context, String label, String value) {
return Row(
children: [
Text(
'$label: ',
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
Expanded(
child: Text(
value,
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
overflow: TextOverflow.ellipsis,
),
),
],
);
}
///
String _formatDateTime(dynamic timestamp) {
if (timestamp == null) return '未知';
try {
DateTime dateTime;
if (timestamp is int) {
// 1013
if (timestamp > 9999999999) {
//
dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp);
} else {
//
dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
}
} else if (timestamp is String) {
dateTime = DateTime.parse(timestamp);
} else {
return '未知';
}
return '${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
} catch (e) {
return '未知';
}
}
///
IconData _getIconData(String iconName) {
switch (iconName) {
case 'phone_android':
return Icons.phone_android;
case 'phone_iphone':
return Icons.phone_iphone;
case 'tablet':
return Icons.tablet_mac;
case 'desktop_mac':
return Icons.desktop_mac;
case 'computer':
return Icons.computer;
default:
return Icons.devices;
}
}
}

View File

@ -1,253 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../localization/app_translations.dart';
import '../../../widgets/kr_app_text_style.dart';
import '../../../widgets/kr_loading_animation.dart';
import '../controllers/kr_home_controller.dart';
import '../models/kr_home_views_status.dart';
import 'kr_home_connection_info_view.dart';
import 'kr_home_connection_options_view.dart';
import 'kr_home_node_list_view.dart';
import '../widgets/kr_subscription_card.dart';
import 'kr_home_trial_card.dart';
import 'kr_home_last_day_card.dart';
import '../../../utils/kr_log_util.dart';
class KRHomeBottomPanel extends GetView<KRHomeController> {
const KRHomeBottomPanel({super.key});
@override
Widget build(BuildContext context) {
return Obx(() {
final currentStatus = controller.kr_currentListStatus.value;
KRLogUtil.kr_i('构建底部面板', tag: 'HomeBottomPanel');
KRLogUtil.kr_i('当前视图状态: ${controller.kr_currentViewStatus.value}',
tag: 'HomeBottomPanel');
KRLogUtil.kr_i('当前高度: ${controller.kr_bottomPanelHeight.value}',
tag: 'HomeBottomPanel');
if (controller.kr_currentListStatus.value ==
KRHomeViewsListStatus.kr_loading) {
return _kr_buildLoadingView();
}
if (controller.kr_currentListStatus.value ==
KRHomeViewsListStatus.kr_error) {
return _kr_buildErrorView(context);
}
if (currentStatus == KRHomeViewsListStatus.kr_serverList ||
currentStatus == KRHomeViewsListStatus.kr_countrySubscribeList ||
currentStatus == KRHomeViewsListStatus.kr_serverSubscribeList ||
currentStatus == KRHomeViewsListStatus.kr_subscribeList) {
return const KRHomeNodeListView();
}
return _kr_buildDefaultView(context);
});
}
Widget _kr_buildDefaultView(BuildContext context) {
// 🔧 Android 15
bool hasValidSubscription = false;
bool isTrial = false;
bool isLastDay = false;
try {
hasValidSubscription = controller.kr_subscribeService.kr_currentSubscribe.value != null;
isTrial = controller.kr_subscribeService.kr_isTrial.value;
isLastDay = controller.kr_subscribeService.kr_isLastDayOfSubscription.value;
} catch (e) {
KRLogUtil.kr_e('获取订阅数据异常: $e', tag: 'HomeBottomPanel');
}
final isNotLoggedIn = controller.kr_currentViewStatus.value ==
KRHomeViewsStatus.kr_notLoggedIn;
KRLogUtil.kr_i('=' * 60, tag: 'HomeBottomPanel');
KRLogUtil.kr_i('🎨 构建默认视图', tag: 'HomeBottomPanel');
KRLogUtil.kr_i('是否未登录: $isNotLoggedIn', tag: 'HomeBottomPanel');
KRLogUtil.kr_i('是否有有效订阅: $hasValidSubscription', tag: 'HomeBottomPanel');
KRLogUtil.kr_i('订阅列表数量: ${controller.kr_subscribeService.kr_availableSubscribes.length}', tag: 'HomeBottomPanel');
KRLogUtil.kr_i('当前选中订阅: ${controller.kr_subscribeService.kr_currentSubscribe.value?.name ?? "null"}', tag: 'HomeBottomPanel');
KRLogUtil.kr_i('是否试用: $isTrial', tag: 'HomeBottomPanel');
KRLogUtil.kr_i('当前高度: ${controller.kr_bottomPanelHeight.value}', tag: 'HomeBottomPanel');
// 🔧 UI
if (hasValidSubscription) {
KRLogUtil.kr_i('✅ 将渲染: 连接信息卡片 (KRHomeConnectionInfoView)', tag: 'HomeBottomPanel');
} else {
KRLogUtil.kr_i('✅ 将渲染: 订阅卡片 (KRSubscriptionCard) - 开通会员界面', tag: 'HomeBottomPanel');
}
KRLogUtil.kr_i('=' * 60, tag: 'HomeBottomPanel');
// 🔧 UI
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// - 使 Expanded + ScrollView
Expanded(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 🔧
if (hasValidSubscription)
//
Builder(builder: (context) {
KRLogUtil.kr_i('🔹 渲染连接信息卡片margin top: ${12}', tag: 'HomeBottomPanel');
return Container(
margin: EdgeInsets.only(top: 12),
child: const KRHomeConnectionInfoView(),
);
})
else
//
Builder(builder: (context) {
KRLogUtil.kr_i('🔹 渲染订阅卡片margin: top=${12}, left=${12}, right=${12}', tag: 'HomeBottomPanel');
return Container(
margin: EdgeInsets.only(top: 12, left: 12, right: 12),
child: const KRSubscriptionCard(),
);
}),
// 2.
if (hasValidSubscription && isTrial)
Container(
margin: EdgeInsets.only(top: 12),
child: const KRHomeTrialCard(),
),
// 3.
if (hasValidSubscription && isLastDay && !isTrial)
Container(
margin: EdgeInsets.only(top: 12),
child: const KRHomeLastDayCard(),
),
// 4. -
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: KRHomeConnectionOptionsView(),
),
],
),
),
),
],
);
}
Widget _kr_buildLoadingView() {
KRLogUtil.kr_i('构建加载视图', tag: 'HomeBottomPanel');
KRLogUtil.kr_i('当前高度: ${controller.kr_bottomPanelHeight.value}',
tag: 'HomeBottomPanel');
// 🔧 Android 15 +
//
return Stack(
children: [
//
Opacity(
opacity: 0.5,
child: _kr_buildDefaultView(Get.context!),
),
//
Center(
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Get.context!.theme.cardColor,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(
color: Colors.green,
strokeWidth: 3.0,
),
SizedBox(height: 12),
Text(
'正在加载...',
style: TextStyle(
fontSize: 14,
color: Get.context!.theme.textTheme.bodyMedium?.color,
),
),
],
),
),
),
],
);
}
Widget _kr_buildErrorView(BuildContext context) {
return Container(
height: 200,
padding: EdgeInsets.all(16),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.error_outline,
size: 48,
color: Theme.of(context).colorScheme.error,
),
SizedBox(height: 16),
Text(
AppTranslations.kr_home.error,
style: KrAppTextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
SizedBox(height: 8),
Text(
AppTranslations.kr_home.checkNetwork,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
SizedBox(height: 24),
SizedBox(
width: 200,
height: 44,
child: ElevatedButton(
onPressed: () => controller.kr_refreshAll(),
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
AppTranslations.kr_home.retry,
style: KrAppTextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
),
);
}
}

View File

@ -1,260 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import '../../../widgets/kr_simple_loading.dart';
import 'package:kaer_with_panels/app/widgets/kr_country_flag.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
import 'package:kaer_with_panels/singbox/model/singbox_status.dart';
import '../controllers/kr_home_controller.dart';
import '../models/kr_home_views_status.dart';
import 'package:flutter/foundation.dart';
class KRHomeConnectionInfoView extends GetView<KRHomeController> {
const KRHomeConnectionInfoView({super.key});
@override
Widget build(BuildContext context) {
return _buildConnectCard(context);
}
///
Widget _buildConnectCard(BuildContext context) {
return Obx(() {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16),
width: double.infinity,
height: 116,
decoration: ShapeDecoration(
color: Theme.of(context).cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
child: Padding(
padding: EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppTranslations.kr_home.currentConnectionTitle,
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
//
GestureDetector(
onTap: () {
controller.kr_switchListStatus(KRHomeViewsListStatus.kr_subscribeList);
},
child: Row(
children: [
Text(
AppTranslations.kr_home.switchNode,
style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodySmall?.color,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
Icon(
Icons.arrow_forward_ios,
size: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
],
),
),
],
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
// 🔧 使 Obx
Obx(() {
final countryCode = controller.kr_getCurrentNodeCountry();
if (kDebugMode) {
print('🌍 ConnectionInfo 更新,国家代码: $countryCode');
}
return KRCountryFlag(
countryCode: countryCode,
);
}),
SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
controller.kr_currentNodeName.value,
style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 6),
Row(
children: [
Obx(() {
final delay = controller.kr_currentNodeLatency.value;
if (kDebugMode) {
print('🔵 UI延迟显示更新: delay=$delay');
}
//
Color getLatencyColor(int delay) {
if (delay == -2) {
return Colors.green;
} else if (delay == -1) {
return Theme.of(context).primaryColor;
} else if (delay < 500) {
return Colors.green;
} else if (delay < 3000) {
return Color(0xFFFFB700);
} else {
return Colors.red;
}
}
//
String getLatencyText(int delay) {
if (delay == -2) {
return '--';
} else if (delay == -1) {
return AppTranslations.kr_home.connecting;
} else if (delay == 0) {
return AppTranslations.kr_home.connected;
} else if (delay >= 3000) {
return AppTranslations.kr_home.timeout;
} else {
return '${delay}ms';
}
}
// 🔧 delay == -1 connecting
if (delay == -1) {
return Row(
children: [
KRSimpleLoading(
color: Colors.green,
size: 12,
duration: const Duration(milliseconds: 800),
),
SizedBox(width: 2),
Text(
AppTranslations.kr_home.connecting,
style: TextStyle(
color: Colors.green,
fontSize: 11,
fontWeight: FontWeight.w400,
),
),
],
);
}
return Row(
children: [
Icon(Icons.signal_cellular_alt,
size: 12,
color: getLatencyColor(delay)),
SizedBox(width: 2),
Text(
getLatencyText(delay),
style: KrAppTextStyle(
color: getLatencyColor(delay),
fontSize: 11,
fontWeight: FontWeight.w400,
),
),
],
);
}),
//
Obx(() {
final delay = controller.kr_currentNodeLatency.value;
if (delay == -1) {
return const SizedBox.shrink();
}
return Row(
children: [
SizedBox(width: 10),
Icon(Icons.arrow_upward,
size: 12,
color: Theme.of(context).textTheme.bodySmall?.color),
Text(
controller.kr_formatBytes(KRSingBoxImp.instance.kr_stats.value.uplink),
style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodySmall?.color,
fontSize: 11,
fontWeight: FontWeight.w400,
),
),
SizedBox(width: 10),
Icon(Icons.arrow_downward,
size: 12,
color: Theme.of(context).textTheme.bodySmall?.color),
Text(
controller.kr_formatBytes(KRSingBoxImp.instance.kr_stats.value.downlink),
style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodySmall?.color,
fontSize: 11,
fontWeight: FontWeight.w400,
),
),
],
);
}),
],
),
],
),
],
),
// 🔧 : 使
Obx(() {
// 🔧 : observable
final _ = KRSingBoxImp.instance.kr_status.value; //
final isConnected = controller.kr_isConnected.value; // 使 controller
//
final status = KRSingBoxImp.instance.kr_status.value;
final isSwitching = status is SingboxStarting || status is SingboxStopping;
// 🔧
if (kDebugMode) {
print('🔵 Switch UI 更新: status=${status.runtimeType}, isConnected=$isConnected, isSwitching=$isSwitching');
}
return CupertinoSwitch(
value: isConnected,
// 🔧 : onChanged nullSwitch
onChanged: isSwitching
? null
: (bool value) {
if (kDebugMode) {
print('🔵 Switch onChanged 触发: 请求=$value, 当前状态=$status');
}
controller.kr_toggleSwitch(value);
},
activeColor: Colors.blue,
);
}),
],
),
],
),
),
);
});
}
}

View File

@ -1,108 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
import 'package:kaer_with_panels/app/routes/app_pages.dart';
import 'package:kaer_with_panels/app/utils/kr_subscribe_navigation_util.dart';
import '../controllers/kr_home_controller.dart';
import '../models/kr_home_views_status.dart';
class KRHomeConnectionOptionsView extends GetView<KRHomeController> {
const KRHomeConnectionOptionsView({super.key});
@override
Widget build(BuildContext context) {
print('🔌 [ConnectionOptions] 开始构建连接选项组件');
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
AppTranslations.kr_home.connectionSectionTitle,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black87,
),
),
const SizedBox(height: 12),
_buildConnectionOption(
"home_ct",
AppTranslations.kr_home.countryRegion,
context,
onTap: () {
controller.kr_switchListStatus(KRHomeViewsListStatus.kr_countrySubscribeList);
},
),
],
);
}
Widget _buildConnectionOption(String icon, String label, BuildContext context,
{VoidCallback? onTap}) {
print('🔌 [ConnectionOptions] 构建连接选项: $label');
return GestureDetector(
onTap: () {
print('🔌 [ConnectionOptions] 选项被点击: $label');
if (controller.kr_subscribeService.kr_currentSubscribe.value == null) {
// 使
KRSubscribeNavigationUtil.navigateToPurchase(tag: 'ConnectionOptions');
} else {
//
onTap?.call();
}
},
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
KrLocalImage(
imageName: icon,
width: 36,
height: 36,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white70
: Colors.black87,
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Text(
label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black87,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Icon(
Icons.arrow_forward_ios,
size: 14,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white54
: Colors.black45,
),
],
),
],
),
),
);
}
}

View File

@ -1,150 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import '../../../routes/app_pages.dart';
import '../../../utils/kr_subscribe_navigation_util.dart';
import '../controllers/kr_home_controller.dart';
///
class KRHomeLastDayCard extends GetView<KRHomeController> {
const KRHomeLastDayCard({super.key});
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16),
width: double.infinity,
decoration: ShapeDecoration(
color: Theme.of(context).cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
child: Padding(
padding: EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
//
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppTranslations.kr_home.lastDaySubscriptionStatus,
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
GestureDetector(
onTap: () => KRSubscribeNavigationUtil.navigateToPurchase(tag: 'LastDayCard'),
child: Row(
children: [
Text(
AppTranslations.kr_home.subscribe,
style: KrAppTextStyle(
color: Colors.blue,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
Icon(
Icons.arrow_forward_ios,
size: 12,
color: Colors.blue,
),
],
),
),
],
),
//
SizedBox(height: 10),
Row(
children: [
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.timer_outlined,
color: Colors.blue,
size: 16,
),
SizedBox(width: 4),
Text(
AppTranslations.kr_home.lastDaySubscriptionMessage,
style: KrAppTextStyle(
fontSize: 12,
color: Colors.blue,
fontWeight: FontWeight.w500,
),
),
],
),
),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Obx(() {
final isLastDay =
controller.kr_subscribeService.kr_isLastDayOfSubscription.value;
final remainingTime = controller.kr_subscribeService.kr_subscriptionRemainingTime.value;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
remainingTime,
style: KrAppTextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: isLastDay
? (DateTime.now().millisecondsSinceEpoch %
2000 <
1000
? Colors.red
: Colors.blue)
: Theme.of(context)
.textTheme
.bodyMedium
?.color,
),
),
SizedBox(height: 4.h),
Text(
AppTranslations.kr_home.subscriptionEndMessage,
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context)
.textTheme
.bodySmall
?.color,
),
),
],
);
}),
],
),
),
],
),
],
),
),
);
}
}

View File

@ -1,950 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import 'package:kaer_with_panels/app/widgets/kr_country_flag.dart';
import '../../../model/business/kr_outbound_item.dart';
import '../../../utils/kr_log_util.dart';
import '../controllers/kr_home_controller.dart';
import '../models/kr_home_views_status.dart';
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
import 'package:kaer_with_panels/app/widgets/kr_network_image.dart';
import '../../../widgets/kr_simple_loading.dart';
import '../../../../singbox/model/singbox_proxy_type.dart';
import 'package:flutter/foundation.dart';
///
///
class KRHomeNodeListView extends GetView<KRHomeController> {
const KRHomeNodeListView({super.key});
//
static const Color krModernGreen = Color(0xFF4CAF50);
static const Color krModernGreenLight = Color(0xFF81C784);
// 🔧
static bool _hasTriggeredAutoTest = false;
///
/// TCP
int _getDisplayDelay(KRHomeController controller, KROutboundItem item) {
//
// VPN使 item.urlTestDelay.value
// - SingBox
// - TCP Socket
return item.urlTestDelay.value;
}
@override
Widget build(BuildContext context) {
return Obx(() {
//
switch (controller.kr_currentListStatus.value) {
case KRHomeViewsListStatus.kr_serverList:
return _buildServerList(context);
case KRHomeViewsListStatus.kr_subscribeList:
return _buildSubscribeList(context);
case KRHomeViewsListStatus.kr_countrySubscribeList:
return _kr_buildRegionList(context);
case KRHomeViewsListStatus.kr_serverSubscribeList:
return _kr_buildServerSubscribeList(context);
default:
return const SizedBox.shrink();
}
});
}
///
///
Widget _buildServerList(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: 360, //
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
child: Column(
children: [
//
Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppTranslations.kr_home.serverListTitle,
style: KrAppTextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
GestureDetector(
onTap: () {
controller.kr_currentListStatus.value =
KRHomeViewsListStatus.kr_none;
},
child: Icon(
Icons.close,
size: 24,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
],
),
),
//
Expanded(
child: Obx(() {
if (controller.kr_subscribeService.groupOutboundList.isEmpty) {
return Center(
child: Text(
AppTranslations.kr_home.noServers,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
);
}
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16),
itemCount: controller.kr_subscribeService.groupOutboundList.length,
itemBuilder: (context, index) {
final group =
controller.kr_subscribeService.groupOutboundList[index];
return Container(
margin: EdgeInsets.only(bottom: 8),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Theme.of(context).dividerColor.withOpacity(0.1),
width: 1,
),
),
child: InkWell(
onTap: () {
controller.kr_setCurrentGroup(group);
controller.kr_currentListStatus.value =
KRHomeViewsListStatus.kr_serverSubscribeList;
},
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: EdgeInsets.all(12),
child: Row(
children: [
KRNetworkImage(
kr_imageUrl: group.icon,
kr_width: 32,
kr_height: 32,
kr_fit: BoxFit.cover,
),
SizedBox(width: 12),
Expanded(
child: Text(
group.tag,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Icon(
Icons.arrow_forward_ios,
size: 16,
color: Theme.of(context).textTheme.bodySmall?.color,
),
],
),
),
),
);
},
);
}),
),
],
),
);
}
///
Widget _kr_buildRegionList(BuildContext context) {
return _kr_buildListPage(
context,
title: AppTranslations.kr_home.countryListTitle,
listContent: Obx(() {
if (controller.kr_subscribeService.groupOutboundList.isEmpty) {
return Center(
child: Text(
AppTranslations.kr_home.noRegions,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
);
}
return _kr_buildListContainer(
context,
child: ListView.builder(
padding: EdgeInsets.fromLTRB(16, 8, 16, 0),
itemCount:
controller.kr_subscribeService.countryOutboundList.length,
itemBuilder: (context, index) {
final country =
controller.kr_subscribeService.countryOutboundList[index];
return Column(
children: [
//
InkWell(
onTap: () {
country.isExpand.value = !country.isExpand.value;
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 12),
child: Row(
children: [
KRCountryFlag(
countryCode: country.country,
width: 40,
height: 40,
),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
controller
.kr_getCountryFullName(country.country),
style: KrAppTextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.textTheme
.bodyMedium
?.color,
),
),
],
),
),
Obx(() {
return Icon(
country.isExpand.value
? Icons.keyboard_arrow_down
: Icons.arrow_forward_ios,
size: 16,
color:
Theme.of(context).textTheme.bodySmall?.color,
);
}),
],
),
),
),
//
Obx(() {
final isExpanded = country.isExpand.value;
if (!isExpanded) return const SizedBox();
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.only(left: 24),
itemCount: country.outboundList.length,
itemBuilder: (context, index) {
final server = country.outboundList[index];
return Column(
children: [
InkWell(
// 🔧 async
onTap: () async {
try {
if (kDebugMode) {
print('🔄 用户点击节点: ${server.tag}');
}
// 使
final success = await controller
.kr_performNodeSwitch(server.tag);
//
if (success) {
controller.kr_currentListStatus.value =
KRHomeViewsListStatus.kr_none;
if (kDebugMode) {
print('✅ 节点切换成功,关闭列表');
}
} else {
if (kDebugMode) {
print('❌ 节点切换失败,列表保持打开');
}
}
} catch (e) {
if (kDebugMode) {
print('❌ 节点切换异常: $e');
}
KRLogUtil.kr_e('节点切换异常: $e',
tag: 'NodeListView');
}
},
child: Container(
padding: EdgeInsets.symmetric(
vertical: 8,
horizontal: 16,
),
decoration: BoxDecoration(
//
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(8),
),
child: _kr_buildNodeListItem(
context,
item: server,
),
),
),
// 线
if (index < country.outboundList.length - 1)
Divider(
height: 1,
indent: 16,
endIndent: 16,
color: Theme.of(context)
.dividerColor
.withOpacity(0.1),
),
],
);
},
);
}),
Divider(
height: 1,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
],
);
},
),
);
}),
);
}
///
//
Widget _kr_buildServerSubscribeList(BuildContext context) {
return _kr_buildListPage(
context,
title: controller.kr_currentGroup.value?.tag ?? '',
onBack: () => controller.kr_currentListStatus.value =
KRHomeViewsListStatus.kr_serverList,
listContent: Obx(() {
final servers = controller.kr_currentGroup.value?.outboundList ?? [];
if (servers.isEmpty) {
return Center(
child: Text(
AppTranslations.kr_home.noNodes,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
);
}
return _kr_buildListContainer(
context,
child: ListView.builder(
padding: EdgeInsets.fromLTRB(16, 16, 16, 0),
itemCount: servers.length,
itemBuilder: (context, index) {
final server = servers[index];
return Column(
children: [
InkWell(
// 🔧 async
onTap: () async {
try {
KRLogUtil.kr_i('🔄 用户点击节点: ${server.tag}');
// 使
final success = await controller
.kr_performNodeSwitch(server.tag);
//
if (success) {
controller.kr_currentListStatus.value =
KRHomeViewsListStatus.kr_none;
KRLogUtil.kr_i('✅ 节点切换成功,关闭列表');
} else {
KRLogUtil.kr_w('❌ 节点切换失败,列表保持打开');
}
} catch (e) {
KRLogUtil.kr_e('❌ 节点切换异常: $e',
tag: 'NodeListView');
}
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 4),
child: _kr_buildNodeListItem(
context,
item: server,
),
),
),
if (index < servers.length - 1)
Divider(
height: 1,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
],
);
},
),
);
}),
);
}
Widget _kr_buildListPage(
BuildContext context, {
required String title,
VoidCallback? onBack,
required Widget listContent,
}) {
return Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_kr_buildTitleBar(
context,
title: title,
onBack: onBack,
onClose: () =>
controller.kr_currentListStatus.value = KRHomeViewsListStatus.kr_none,
),
SizedBox(height: 16),
Expanded(
child: Column(
children: [
Expanded(child: listContent),
//
SizedBox(height: 12),
],
),
),
],
),
);
}
//
Widget _kr_buildTitleBar(
BuildContext context, {
required String title,
VoidCallback? onBack,
VoidCallback? onClose,
}) {
return Padding(
padding: EdgeInsets.only(left: 16, right: 16, top: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
if (onBack != null) ...[
GestureDetector(
onTap: onBack,
child: Icon(
Icons.arrow_back_ios,
size: 20,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
SizedBox(width: 8),
],
Text(
title,
style: KrAppTextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
],
),
if (onClose != null)
GestureDetector(
onTap: onClose,
child: Icon(
Icons.close,
size: 24,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
],
),
);
}
///
Widget _kr_buildListContainer(
BuildContext context, {
required Widget child,
}) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12),
),
child: child,
);
}
///
Widget _kr_buildNodeListItem(
BuildContext context, {
required KROutboundItem item,
}) {
//
Color getLatencyColor(int delay) {
if (delay == 0) {
return Colors.transparent;
} else if (delay < 500) {
return krModernGreen;
} else if (delay < 3000) {
return Color(0xFFFFB700); // 使
} else {
return Colors.red;
}
}
return Container(
key: ValueKey(item.id),
padding: EdgeInsets.symmetric(vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// 🔧
KRCountryFlag(
countryCode: item.country,
width: 36,
height: 36,
),
SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
item.tag,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
Obx(
() => controller.kr_cutTag.value == item.tag
? Container(
margin: EdgeInsets.only(left: 4),
padding: EdgeInsets.symmetric(
horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color: krModernGreenLight.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
AppTranslations.kr_home.selected,
style: KrAppTextStyle(
fontSize: 10,
color: krModernGreen,
fontWeight: FontWeight.w500,
),
),
)
: const SizedBox.shrink(),
),
],
),
SizedBox(height: 2),
Text(
item.city,
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
],
),
),
//
GetBuilder<KRHomeController>(
id: item.tag,
builder: (controller) {
//
int displayDelay = _getDisplayDelay(controller, item);
return Container(
alignment: Alignment.center,
child: Text(
displayDelay == 0
? ''
: displayDelay >= 3000
? AppTranslations.kr_home.timeout
: '${displayDelay}ms',
style: KrAppTextStyle(
fontSize: 12,
color: getLatencyColor(displayDelay),
),
),
);
},
),
],
),
);
}
//
Widget _buildSubscribeList(BuildContext context) {
return _kr_buildListPage(
context,
title: AppTranslations.kr_home.nodeListTitle,
listContent: Obx(() {
if (controller.kr_subscribeService.allList.isEmpty) {
return Center(
child: Text(
AppTranslations.kr_home.noNodes,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
);
}
// 🔧
if (!_hasTriggeredAutoTest) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!controller.kr_isConnected.value && !controller.kr_isLatency.value && !_hasTriggeredAutoTest) {
_hasTriggeredAutoTest = true; //
KRLogUtil.kr_i('🔄 节点列表显示 - 自动触发延迟测试(首次)', tag: 'NodeListView');
controller.kr_urlTest();
}
});
}
return _kr_buildListContainer(
context,
child: ListView(
padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
children: [
//
InkWell(
onTap: () => controller.kr_urlTest(),
child: Container(
padding: EdgeInsets.symmetric(vertical: 8),
margin: EdgeInsets.only(top: 8), //
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: krModernGreenLight.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Center(
child: controller.kr_isLatency.value
? KRSimpleLoading(
color: krModernGreen,
size: 24,
duration: const Duration(milliseconds: 800),
)
: Icon(
Icons.speed,
size: 24,
color: krModernGreen,
),
),
),
SizedBox(width: 8),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
controller.kr_isLatency.value
? AppTranslations.kr_home.testing
: AppTranslations.kr_home.testLatency,
style: KrAppTextStyle(
fontSize: 14,
color: controller.kr_isLatency.value
? Theme.of(context)
.textTheme
.bodySmall
?.color
: Theme.of(context)
.textTheme
.bodyMedium
?.color,
fontWeight: controller.kr_isLatency.value
? FontWeight.normal
: FontWeight.w500,
),
),
if (!controller.kr_isLatency.value) ...[
SizedBox(height: 2),
Text(
AppTranslations.kr_home.refreshLatencyDesc,
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context)
.textTheme
.bodySmall
?.color,
),
),
],
],
),
),
if (!controller.kr_isLatency.value)
Icon(
Icons.arrow_forward_ios,
size: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
],
),
),
),
// 线
Divider(
height: 16,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
// Auto
InkWell(
// 🔧 async
onTap: () async {
try {
final success =
await controller.kr_performNodeSwitch('auto');
if (success) {
controller.kr_currentListStatus.value =
KRHomeViewsListStatus.kr_none;
}
} catch (e) {
KRLogUtil.kr_e('Auto选项切换异常: $e',
tag: 'NodeListView');
}
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
KrLocalImage(
imageName: "home_list_location",
width: 36,
height: 36,
color: controller.kr_cutTag.value == 'auto'
? Colors.green
: null,
),
SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
AppTranslations.kr_home.autoSelect,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context)
.textTheme
.bodyMedium
?.color,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
if (controller.kr_cutTag.value == 'auto')
Container(
margin: EdgeInsets.only(left: 4),
padding: EdgeInsets.symmetric(
horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color:
krModernGreenLight.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
AppTranslations.kr_home.selected,
style: KrAppTextStyle(
fontSize: 10,
color: krModernGreen,
fontWeight: FontWeight.w500,
),
),
),
],
),
SizedBox(height: 2),
Obx(() {
//
String selectedNode =
AppTranslations.kr_home.autoSelect;
int delay = 0;
for (var group
in KRSingBoxImp.instance.kr_activeGroups) {
if (group.type == ProxyType.urltest) {
selectedNode = group.selected;
delay = controller
.kr_subscribeService.keyList[group.selected]
?.urlTestDelay.value ??
0;
break;
}
}
return Text(
selectedNode,
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context)
.textTheme
.bodySmall
?.color,
),
);
}),
],
),
),
Obx(() {
//
String selectedNode =
AppTranslations.kr_home.autoSelect;
int delay = 0;
for (var group
in KRSingBoxImp.instance.kr_activeGroups) {
if (group.type == ProxyType.urltest) {
selectedNode = group.selected;
delay = controller
.kr_subscribeService
.keyList[group.selected]
?.urlTestDelay
.value ??
0;
break;
}
}
return delay > 0
? Container(
alignment: Alignment.center,
child: Text(
delay < 3000
? '${delay}ms'
: AppTranslations.kr_home.timeout,
style: KrAppTextStyle(
fontSize: 12,
color: delay < 3000
? Colors.green
: Colors.red,
),
),
)
: const SizedBox.shrink();
}),
],
),
),
),
// 线
Divider(
height: 16,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
//
...controller.kr_subscribeService.allList
.map((node) => Column(
children: [
InkWell(
// 🔧 async
onTap: () async {
try {
KRLogUtil.kr_i(
'🔄 用户点击节点: ${node.tag}');
final success = await controller
.kr_performNodeSwitch(node.tag);
if (success) {
controller.kr_currentListStatus.value =
KRHomeViewsListStatus.kr_none;
}
} catch (e) {
KRLogUtil.kr_e(
'节点切换异常: $e',
tag: 'NodeListView');
}
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 4),
child: _kr_buildNodeListItem(
context,
item: node,
),
),
),
if (node !=
controller.kr_subscribeService.allList.last)
Divider(
height: 1,
color: Theme.of(context)
.dividerColor
.withOpacity(0.1),
),
],
))
.toList(),
],
),
);
}),
);
}
}

View File

@ -1,223 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'dart:math';
import 'package:kaer_with_panels/app/modules/kr_home/controllers/kr_home_controller.dart';
import 'package:kaer_with_panels/app/common/app_run_data.dart';
import 'package:kaer_with_panels/app/model/response/kr_user_available_subscribe.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:kaer_with_panels/app/widgets/dialogs/kr_dialog.dart';
class KRHomeSubscriptionView extends GetView<KRHomeController> {
const KRHomeSubscriptionView({super.key});
@override
Widget build(BuildContext context) {
return Obx(() {
if (!KRAppRunData().kr_isLogin.value) {
return const SizedBox.shrink();
}
final currentSubscribe =
controller.kr_subscribeService.kr_currentSubscribe.value;
if (currentSubscribe == null) {
return const SizedBox.shrink();
}
KRLogUtil.kr_i('当前订阅名称: ${currentSubscribe.name}',
tag: 'SubscriptionView');
final totalTraffic = currentSubscribe.traffic;
final usedTraffic = currentSubscribe.download + currentSubscribe.upload;
final hasTrafficLimit = totalTraffic > 0;
var trafficPercentage =
hasTrafficLimit ? (usedTraffic / totalTraffic).clamp(0.0, 1.0) : 0.0;
return Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.bolt_rounded,
size: 14.w,
color: Theme.of(context).brightness == Brightness.light
? Colors.black54
: Colors.white54,
),
SizedBox(width: 4.w),
Expanded(
child: Text(
currentSubscribe.name,
style: TextStyle(
color: Theme.of(context).brightness == Brightness.light
? Colors.black
: Colors.white,
fontSize: 12.sp,
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
SizedBox(width: 4.w),
Container(
height: 3.h,
width: 20.w,
decoration: BoxDecoration(
color: Theme.of(context).brightness == Brightness.light
? Colors.grey[200]
: Colors.grey[800],
borderRadius: BorderRadius.circular(1.5.r),
),
child: FractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor: trafficPercentage,
child: Container(
decoration: BoxDecoration(
color: _getTrafficColor(trafficPercentage),
borderRadius: BorderRadius.circular(1.5.r),
),
),
),
),
SizedBox(width: 4.w),
Icon(
Icons.swap_horiz,
size: 14.w,
color: Theme.of(context).brightness == Brightness.light
? Colors.black.withOpacity(0.5)
: Colors.white.withOpacity(0.5),
),
],
),
);
});
}
Widget _buildLoadingView(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Theme.of(context).brightness == Brightness.light
? Colors.white
: Colors.grey[900]!,
Theme.of(context).brightness == Brightness.light
? Colors.grey[50]!
: Colors.grey[800]!,
],
),
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 15,
offset: const Offset(0, 5),
),
],
),
child: Row(
children: [
const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
const SizedBox(width: 16),
Text(
AppTranslations.kr_home.loading,
style: TextStyle(
color: Theme.of(context).brightness == Brightness.light
? Colors.black
: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
],
),
);
}
IconData _getStatusIcon(KRUserAvailableSubscribeItem subscribe) {
final now = DateTime.now();
final expireTime = DateTime.parse(subscribe.expireTime);
final difference = expireTime.difference(now);
if (difference.isNegative) {
return Icons.error_outline_rounded;
} else if (difference.inDays <= 1) {
return Icons.warning_amber_rounded;
} else {
return Icons.check_circle_outline_rounded;
}
}
Color _getStatusColor(
BuildContext context, KRUserAvailableSubscribeItem subscribe) {
final now = DateTime.now();
final expireTime = DateTime.parse(subscribe.expireTime);
final difference = expireTime.difference(now);
if (difference.isNegative) {
return Colors.red;
} else if (difference.inDays <= 1) {
return Colors.orange;
} else {
return const Color(0xFF00E52B);
}
}
String _getStatusText(KRUserAvailableSubscribeItem subscribe) {
final now = DateTime.now();
final expireTime = DateTime.parse(subscribe.expireTime);
final difference = expireTime.difference(now);
if (difference.isNegative) {
return '已过期';
} else if (difference.inDays <= 1) {
return '即将到期';
} else {
return '有效';
}
}
Color _getTrafficColor(double percentage) {
if (percentage >= 0.9) {
return Colors.red;
} else if (percentage >= 0.7) {
return Colors.orange;
} else {
return const Color(0xFF00E52B);
}
}
String _formatTraffic(int bytes) {
if (bytes < 1024) {
return '$bytes B';
} else if (bytes < 1024 * 1024) {
return '${(bytes / 1024).toStringAsFixed(1)} KB';
} else if (bytes < 1024 * 1024 * 1024) {
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
} else {
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB';
}
}
String _formatDate(String dateStr) {
try {
final date = DateTime.parse(dateStr);
return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
} catch (e) {
return dateStr;
}
}
}

View File

@ -1,149 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import '../../../routes/app_pages.dart';
import '../../../services/kr_subscribe_service.dart';
import '../../../utils/kr_log_util.dart';
import '../../../utils/kr_subscribe_navigation_util.dart';
import '../controllers/kr_home_controller.dart';
///
class KRHomeTrialCard extends GetView<KRHomeController> {
const KRHomeTrialCard({super.key});
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16),
width: double.infinity,
decoration: ShapeDecoration(
color: Theme.of(context).cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
child: Padding(
padding: EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
//
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppTranslations.kr_home.trialStatus,
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
GestureDetector(
onTap: () => KRSubscribeNavigationUtil.navigateToPurchase(tag: 'TrialCard'),
child: Row(
children: [
Text(
AppTranslations.kr_home.subscribe,
style: KrAppTextStyle(
color: Colors.blue,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
Icon(
Icons.arrow_forward_ios,
size: 12,
color: Colors.blue,
),
],
),
),
],
),
//
SizedBox(height: 10),
Row(
children: [
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.timer_outlined,
color: Colors.blue,
size: 16,
),
SizedBox(width: 4),
Text(
AppTranslations.kr_home.trialing,
style: KrAppTextStyle(
fontSize: 12,
color: Colors.blue,
fontWeight: FontWeight.w500,
),
),
],
),
),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCountdown(),
],
),
),
],
),
],
),
),
);
}
Widget _buildCountdown() {
return Obx(() {
final subscribeService = KRSubscribeService();
final remainingTime = subscribeService.kr_trialRemainingTime.value;
final isExpired = remainingTime.isEmpty;
return Builder(
builder: (context) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
remainingTime,
style: KrAppTextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: isExpired
? (DateTime.now().millisecondsSinceEpoch % 2000 < 1000
? Colors.red
: Colors.blue)
: Theme.of(context).textTheme.bodyMedium?.color,
),
),
SizedBox(height: 4.h),
Text(
AppTranslations.kr_home.trialEndMessage,
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
],
),
);
});
}
}

View File

@ -11,9 +11,6 @@ import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart';
import '../../../services/kr_subscribe_service.dart'; import '../../../services/kr_subscribe_service.dart';
import '../controllers/kr_home_controller.dart'; import '../controllers/kr_home_controller.dart';
import '../models/kr_home_views_status.dart'; import '../models/kr_home_views_status.dart';
import '../widgets/kr_subscribe_selector_view.dart';
import 'kr_home_bottom_panel.dart';
import 'kr_home_subscription_view.dart';
import './hi_animated_connect_button.dart'; import './hi_animated_connect_button.dart';
import 'package:kaer_with_panels/app/services/global_overlay_service.dart'; import 'package:kaer_with_panels/app/services/global_overlay_service.dart';
@ -138,18 +135,15 @@ class _KRHomeViewState extends State<KRHomeView> {
style: highlightStyle, style: highlightStyle,
); );
} else { } else {
// --- 2.2: --- // --- 2.2: ---
final difference = final formattedExpireTime = expireDateTime != null
expireDateTime?.difference(now); ? '${expireDateTime.year}/${expireDateTime.month.toString().padLeft(2, '0')}/${expireDateTime.day.toString().padLeft(2, '0')} ${expireDateTime.hour.toString().padLeft(2, '0')}:${expireDateTime.minute.toString().padLeft(2, '0')}'
final remainingDaysText = : '未知';
(difference?.inDays ?? 0) > 0 // 使 \n Text
? '${difference!.inDays}' content = Text(
: '不足一天'; '套餐到期时间:$formattedExpireTime\n${controller.kr_isConnected.value ? '当前线路:${controller.kr_getRealConnectedNodeCountry()}' : '未连接'}',
// 使 \n Text style: normalStyle,
content = Text( );
'套餐剩余:$remainingDaysText\n${controller.kr_isConnected.value ? '当前线路:${controller.kr_getRealConnectedNodeCountry()}' : '未连接'}',
style: normalStyle,
);
} }
} }
@ -221,7 +215,7 @@ class _KRHomeViewState extends State<KRHomeView> {
GestureDetector( GestureDetector(
onTap: () { onTap: () {
HIDialog.show( HIDialog.show(
title: '*闪连功能', title: '闪连功能',
message: message:
'开启后,每次打开软件默认自动连接,无需点击连接按钮\n在后台关闭软件后,软件将自动断开', '开启后,每次打开软件默认自动连接,无需点击连接按钮\n在后台关闭软件后,软件将自动断开',
); );

View File

@ -1,275 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:kaer_with_panels/app/modules/kr_home/controllers/kr_home_controller.dart';
import 'package:kaer_with_panels/app/model/response/kr_user_available_subscribe.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
class KRSubscribeSelectorView extends StatelessWidget {
final KRHomeController? controller;
const KRSubscribeSelectorView({
super.key,
this.controller,
});
@override
Widget build(BuildContext context) {
final homeController = controller ?? Get.find<KRHomeController>();
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Container(
width: MediaQuery.of(context).size.width * 0.85,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(20.r),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10.r,
offset: Offset(0, 2.w),
spreadRadius: 0,
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.05),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.r),
topRight: Radius.circular(20.r),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppTranslations.kr_purchaseMembership.selectPackage,
style: KrAppTextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Theme.of(context).textTheme.titleLarge?.color,
),
),
Material(
color: Colors.transparent,
child: InkWell(
onTap: () => Navigator.pop(context),
borderRadius: BorderRadius.circular(20.r),
child: Padding(
padding: EdgeInsets.all(4.w),
child: Icon(
Icons.close_rounded,
color: Theme.of(context).textTheme.bodyLarge?.color?.withOpacity(0.6),
size: 18.w,
),
),
),
),
],
),
),
Obx(() {
final subscribes = homeController.kr_subscribeService.kr_availableSubscribes;
if (subscribes.isEmpty) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 12.w),
child: Column(
children: [
Icon(
Icons.subscriptions_outlined,
size: 48.w,
color: Theme.of(context).textTheme.bodyLarge?.color?.withOpacity(0.3),
),
SizedBox(height: 12.h),
Text(
AppTranslations.kr_purchaseMembership.noData,
style: KrAppTextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyLarge?.color?.withOpacity(0.5),
),
),
],
),
);
}
return ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.5,
),
child: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.symmetric(vertical: 4.h, horizontal: 4.w),
itemCount: subscribes.length,
itemBuilder: (context, index) {
final subscribe = subscribes[index];
final isCurrent = subscribe.id == homeController.kr_subscribeService.kr_currentSubscribe.value?.id;
return _SubscribeItem(
subscribe: subscribe,
isCurrent: isCurrent,
onTap: () {
homeController.kr_switchSubscribe(subscribe);
Get.back();
},
);
},
),
);
}),
SizedBox(height: 8.h),
],
),
);
}
}
class _SubscribeItem extends StatelessWidget {
final KRUserAvailableSubscribeItem subscribe;
final bool isCurrent;
final VoidCallback onTap;
const _SubscribeItem({
required this.subscribe,
required this.isCurrent,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final usedTraffic = (subscribe.download + subscribe.upload) / 1024 / 1024 / 1024;
final totalTraffic = subscribe.traffic / 1024 / 1024 / 1024;
var percentage = totalTraffic > 0 ? usedTraffic / totalTraffic : 0.0;
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
final isUnlimited = subscribe.traffic == 0;
String getUsedTrafficDisplay() {
if (usedTraffic < 1) {
return '${(usedTraffic * 1024).toStringAsFixed(2)}MB';
} else {
return '${usedTraffic.toStringAsFixed(2)}GB';
}
}
return Padding(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12.r),
child: Ink(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: isCurrent
? Colors.blue.withOpacity(isDarkMode ? 0.15 : 0.08)
: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: isCurrent
? Colors.blue.withOpacity(isDarkMode ? 0.5 : 0.3)
: Theme.of(context).dividerColor.withOpacity(0.3),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
subscribe.name,
style: KrAppTextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Theme.of(context).textTheme.titleLarge?.color,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
if (isCurrent)
Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(16.r),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.2),
blurRadius: 6.w,
offset: Offset(0, 1.w),
),
],
),
child: Text(
AppTranslations.kr_home.currentConnectionTitle,
style: KrAppTextStyle(
color: Colors.white,
fontSize: 11,
fontWeight: FontWeight.w600,
),
),
),
],
),
SizedBox(height: 8.h),
Text(
isUnlimited
? AppTranslations.kr_purchaseMembership.unlimitedTraffic
: '${getUsedTrafficDisplay()} / ${totalTraffic.toStringAsFixed(2)}GB',
style: KrAppTextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyMedium?.color?.withOpacity(0.8),
),
),
if (!isUnlimited) ...[
SizedBox(height: 8.h),
ClipRRect(
borderRadius: BorderRadius.circular(4.r),
child: LinearProgressIndicator(
value: percentage.clamp(0.0, 1.0),
backgroundColor: isDarkMode
? Colors.grey[700]?.withOpacity(0.7)
: Colors.grey[300]?.withOpacity(0.9),
valueColor: AlwaysStoppedAnimation<Color>(
_getTrafficColor(percentage, isDarkMode),
),
minHeight: 4.w,
),
),
],
],
),
),
),
),
);
}
Color _getTrafficColor(double percentage, bool isDarkMode) {
if (percentage >= 0.9) {
return isDarkMode
? Colors.red.withOpacity(0.8)
: Colors.red.withOpacity(0.7);
} else if (percentage >= 0.7) {
return isDarkMode
? Colors.orange.withOpacity(0.8)
: Colors.orange.withOpacity(0.7);
} else {
return isDarkMode
? Colors.blue.withOpacity(0.8)
: Colors.blue.withOpacity(0.7);
}
}
}

View File

@ -1,116 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/routes/app_pages.dart';
import 'package:kaer_with_panels/app/utils/kr_subscribe_navigation_util.dart';
import '../../../widgets/kr_app_text_style.dart';
///
class KRSubscriptionCard extends StatelessWidget {
const KRSubscriptionCard({
super.key,
});
@override
Widget build(BuildContext context) {
return _kr_buildSubscriptionCard(context);
}
//
Widget _kr_buildSubscriptionCard(BuildContext context) {
// 🔧 ScreenUtil使
return Container(
//
constraints: const BoxConstraints(minHeight: 200),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
//
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
shape: BoxShape.circle,
),
child: const Icon(
Icons.language,
color: Colors.blue,
size: 28,
),
),
const SizedBox(height: 16),
//
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
AppTranslations.kr_home.subscriptionDescription,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
height: 1.5,
// 🔧
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black87,
),
),
),
const SizedBox(height: 20),
//
SizedBox(
width: double.infinity,
height: 46,
child: ElevatedButton(
onPressed: () {
KRSubscribeNavigationUtil.navigateToPurchase(tag: 'SubscriptionCard');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
AppTranslations.kr_home.subscribe,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
),
],
),
);
}
Widget _kr_buildListContainer(
BuildContext context, {
required Widget child,
EdgeInsetsGeometry? margin,
bool addBottomPadding = true,
}) {
return Container(
margin: margin ?? EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12),
),
child: IntrinsicWidth(
child: child,
),
);
}
}

View File

@ -43,6 +43,7 @@ class KRInviteController extends GetxController {
totalCommission: 0, totalCommission: 0,
).obs; ).obs;
final kr_referCode = ''.obs; final kr_referCode = ''.obs;
final kr_refererId = 0.obs;
final kr_isLoading = false.obs; final kr_isLoading = false.obs;
final count = 0.obs; final count = 0.obs;
final EasyRefreshController refreshController = EasyRefreshController(); final EasyRefreshController refreshController = EasyRefreshController();
@ -65,6 +66,7 @@ class KRInviteController extends GetxController {
totalCommission: 0, totalCommission: 0,
); );
kr_referCode.value = ''; kr_referCode.value = '';
kr_refererId.value = 0;
} }
}); });
if (KRAppRunData.getInstance().kr_isLogin.value) { if (KRAppRunData.getInstance().kr_isLogin.value) {
@ -87,11 +89,14 @@ class KRInviteController extends GetxController {
KRLogUtil.kr_i(' - kr_isLogin: ${appData.kr_isLogin.value}', tag: 'InviteController'); KRLogUtil.kr_i(' - kr_isLogin: ${appData.kr_isLogin.value}', tag: 'InviteController');
KRLogUtil.kr_i(' - kr_account: ${appData.kr_account.value}', tag: 'InviteController'); KRLogUtil.kr_i(' - kr_account: ${appData.kr_account.value}', tag: 'InviteController');
KRLogUtil.kr_i(' - kr_referCode: ${appData.kr_referCode.value}', tag: 'InviteController'); KRLogUtil.kr_i(' - kr_referCode: ${appData.kr_referCode.value}', tag: 'InviteController');
KRLogUtil.kr_i(' - kr_refererId: ${appData.kr_refererId.value}', tag: 'InviteController');
KRLogUtil.kr_i(' - kr_balance: ${appData.kr_balance.value}', tag: 'InviteController'); KRLogUtil.kr_i(' - kr_balance: ${appData.kr_balance.value}', tag: 'InviteController');
KRLogUtil.kr_i(' - kr_commission: ${appData.kr_commission.value}', tag: 'InviteController'); KRLogUtil.kr_i(' - kr_commission: ${appData.kr_commission.value}', tag: 'InviteController');
kr_referCode.value = appData.kr_referCode.value; kr_referCode.value = appData.kr_referCode.value;
kr_refererId.value = appData.kr_refererId.value;
KRLogUtil.kr_i('📋 [InviteController] 获取到邀请码: "${kr_referCode.value}"', tag: 'InviteController'); KRLogUtil.kr_i('📋 [InviteController] 获取到邀请码: "${kr_referCode.value}"', tag: 'InviteController');
KRLogUtil.kr_i('📋 [InviteController] 获取到邀请人ID: ${kr_refererId.value}', tag: 'InviteController');
if (kr_referCode.value.isEmpty) { if (kr_referCode.value.isEmpty) {
KRLogUtil.kr_w('⚠️ [InviteController] 邀请码为空!', tag: 'InviteController'); KRLogUtil.kr_w('⚠️ [InviteController] 邀请码为空!', tag: 'InviteController');
@ -148,19 +153,23 @@ class KRInviteController extends GetxController {
/// ///
Future<void> kr_handleBindInviteCode() async { Future<void> kr_handleBindInviteCode() async {
final text = otherInviteCodeController.text; final text = otherInviteCodeController.text;
print('输入的邀请码是: $text');
if (text.isEmpty) { if (text.isEmpty) {
KRCommonUtil.kr_showToast('请输入邀请码'); KRCommonUtil.kr_showToast('请输入邀请码');
return; return;
} }
if (text.trim().toLowerCase() == kr_referCode.value.trim().toLowerCase()) {
KRCommonUtil.kr_showToast('您不可以邀请自己');
return;
}
try { try {
final either = await KRUserApi().hi_inviteCode(text); final either = await KRUserApi().hi_inviteCode(text);
either.fold( either.fold(
(error) => KRCommonUtil.kr_showToast(error.msg), (error) => KRCommonUtil.kr_showToast(error.msg),
(affiliateCount) { (success) {
KRCommonUtil.kr_showToast('绑定成功: $text'); KRCommonUtil.kr_showToast('绑定成功: $text');
otherInviteCodeController.text = ''; otherInviteCodeController.text = '';
_kr_fetchUserInfo(); // refererId
}, },
); );
} catch (e) { } catch (e) {

View File

@ -29,187 +29,201 @@ class KRInviteView extends GetView<KRInviteController> {
children: [ children: [
// 1. / // 1. /
Positioned.fill( Positioned.fill(
child: SingleChildScrollView( child: LayoutBuilder(
// builder: (context, constraints) {
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.manual, return SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
child: Padding( child: ConstrainedBox(
padding: EdgeInsets.symmetric(horizontal: 40.w), constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: Column( child: IntrinsicHeight(
crossAxisAlignment: CrossAxisAlignment.start, child: Padding(
children: [ padding: EdgeInsets.symmetric(horizontal: 40.w),
SizedBox(height: 20.w), child: Column(
// 🟢 crossAxisAlignment: CrossAxisAlignment.start,
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.w),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(25.r),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
KrLocalImage(
imageName: 'hi-home-logo',
imageType: ImageType.svg,
width: 54.w,
color: Colors.black,
),
SizedBox(width: 16.w),
Flexible(
child: Text(
'受邀用户首次付款时他将与您分别获得3天免费使用时长',
style: TextStyle(
color: Colors.black,
fontSize: 14.sp,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
SizedBox(height: 26.w),
// 🟢
Container(
padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 2.w),
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 2.0),
borderRadius: BorderRadius.circular(1000.r),
),
child: Obx(
() => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
SizedBox(height: 20.w),
// 🟢
Container( Container(
width: 100.w, width: double.infinity,
height: 40.w, padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.w),
alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(25.r),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
KrLocalImage(
imageName: 'hi-home-logo',
imageType: ImageType.svg,
width: 54.w,
color: Colors.black,
),
SizedBox(width: 16.w),
Flexible(
child: Text(
'受邀用户首次付款时他将与您分别获得3天免费使用时长',
style: TextStyle(
color: Colors.black,
fontSize: 14.sp,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
SizedBox(height: 26.w),
// 🟢
Container(
padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 2.w),
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 2.0),
borderRadius: BorderRadius.circular(1000.r), borderRadius: BorderRadius.circular(1000.r),
), ),
child: Text( child: Obx(
'邀请码', () => Row(
style: TextStyle( mainAxisAlignment: MainAxisAlignment.spaceBetween,
color: Colors.black, children: [
fontSize: 16.sp, Container(
fontWeight: FontWeight.w600, width: 100.w,
height: 40.w,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(1000.r),
),
child: Text(
'邀请码',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
),
),
Expanded(
child: Text(
controller.kr_referCode.value,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 20.sp,
fontWeight: FontWeight.bold,
),
),
),
IconButton(
icon: const KrLocalImage(
imageName: 'share-icon',
imageType: ImageType.svg,
color: Colors.white,
),
onPressed: () {
if (controller.kr_referCode.value.isNotEmpty) {
final code = controller.kr_referCode.value;
final text = '#您的好友邀请您使用Hi快网络加速器\n'
'安装完毕后,在软件内<邀请好友>页面粘贴以下邀请码\n'
'$code\n'
'您和您的好友将会分别获得3天免费时长\n\n'
'点击此处进入下载页面\n'
'或在浏览器输入hifastvpn.com下载#';
if (GetPlatform.isIOS) {
Share.share(text, subject: '直接分享Hi快VPN邀请链接');
} else {
Clipboard.setData(ClipboardData(text: text));
KRCommonUtil.kr_showToast(AppTranslations.kr_invite.inviteCodeCopied);
}
}
},
),
],
), ),
), ),
), ),
Expanded(
child: Text( const Spacer(),
controller.kr_referCode.value,
textAlign: TextAlign.center, // 🟢
style: TextStyle( Obx(() {
color: Colors.white, //
fontSize: 20.sp, if (controller.kr_refererId.value != 0) {
fontWeight: FontWeight.bold, return const SizedBox.shrink();
), }
), return Column(
), crossAxisAlignment: CrossAxisAlignment.start,
IconButton( children: [
icon: const KrLocalImage( Text(
imageName: 'share-icon', '接受他人邀请',
imageType: ImageType.svg, style: TextStyle(
color: Colors.white, color: Colors.white,
), fontSize: 14.sp,
onPressed: () { fontWeight: FontWeight.bold,
if (controller.kr_referCode.value.isNotEmpty) { ),
final code = controller.kr_referCode.value; ),
final text = '#您的好友邀请您使用Hi快网络加速器\n' SizedBox(height: 8.h),
'安装完毕后,在软件内<邀请好友>页面粘贴以下邀请码\n' RepaintBoundary(
'$code\n' child: TextField(
'您和您的好友将会分别获得3天免费时长\n\n' controller: controller.otherInviteCodeController,
'点击此处进入下载页面\n' textAlign: TextAlign.center,
'或在浏览器输入hifastvpn.com下载#'; style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
if (GetPlatform.isIOS) { decoration: InputDecoration(
Share.share(text, subject: '直接分享Hi快VPN邀请链接'); hintText: '填入邀请人邀请码兑换免费时长...',
} else { hintStyle: const TextStyle(color: Color(0xFFA6A6A6)),
Clipboard.setData(ClipboardData(text: text)); filled: true,
KRCommonUtil.kr_showToast(AppTranslations.kr_invite.inviteCodeCopied); fillColor: Colors.transparent,
} contentPadding: EdgeInsets.symmetric(horizontal: 22.w),
} border: OutlineInputBorder(
}, borderRadius: BorderRadius.circular(1000.r),
), borderSide: const BorderSide(color: Colors.white, width: 2.0),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(1000.r),
borderSide: const BorderSide(color: Colors.white, width: 2.0),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(1000.r),
borderSide: const BorderSide(color: Colors.white, width: 2.0),
),
constraints: BoxConstraints(maxHeight: 50.h),
),
),
),
SizedBox(height: 10.w),
SizedBox(
width: double.infinity,
height: 50.w,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(1000.r),
),
),
onPressed: () => controller.kr_handleBindInviteCode(),
child: Text(
'保存',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
),
),
],
);
}),
//
SizedBox(height: 90.w),
], ],
), ),
), ),
), ),
SizedBox(height: 160.w), ),
// 🟢 );
Column( },
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'接受他人邀请',
style: TextStyle(
color: Colors.white,
fontSize: 14.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8.h),
// 使 RepaintBoundary TextField
RepaintBoundary(
child: TextField(
controller: controller.otherInviteCodeController,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
decoration: InputDecoration(
hintText: '填入邀请人邀请码兑换免费时长...',
hintStyle: const TextStyle(color: Color(0xFFA6A6A6)),
filled: true,
fillColor: Colors.transparent,
contentPadding: EdgeInsets.symmetric(horizontal: 22.w),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(1000.r),
borderSide: const BorderSide(color: Colors.white, width: 2.0),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(1000.r),
borderSide: const BorderSide(color: Colors.white, width: 2.0),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(1000.r),
borderSide: const BorderSide(color: Colors.white, width: 2.0),
),
constraints: BoxConstraints(maxHeight: 50.h),
),
),
),
SizedBox(height: 10.w),
SizedBox(
width: double.infinity,
height: 50.w,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(1000.r),
),
),
onPressed: () => controller.kr_handleBindInviteCode(),
child: Text(
'保存',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
//
SizedBox(height: 250.w),
],
),
),
), ),
), ),

View File

@ -1,12 +0,0 @@
import 'package:get/get.dart';
import '../controllers/kr_language_selector_controller.dart';
class KRLanguageSelectorBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<KRLanguageSelectorController>(
() => KRLanguageSelectorController(),
);
}
}

View File

@ -1,42 +0,0 @@
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/localization/kr_language_utils.dart';
class KRLanguageSelectorController extends GetxController {
// 使 KRLanguage
final RxList<KRLanguage> kr_languages = <KRLanguage>[].obs;
//
final RxString kr_selectedLanguage = ''.obs;
@override
void onInit() {
super.onInit();
kr_selectedLanguage.value = KRLanguageUtils.getCurrentLanguage().countryCode;
kr_loadLanguages();
}
//
void kr_loadLanguages() {
//
final sortedLanguages = KRLanguage.values.toList()
..sort((a, b) => a == KRLanguage.en ? -1 : 1);
kr_languages.value = sortedLanguages;
}
//
Future<void> kr_selectLanguage(KRLanguage language) async {
try {
//
kr_selectedLanguage.value = language.countryCode;
//
await KRLanguageUtils.switchLanguage(language);
} catch (err) {
Get.snackbar(
'错误',
'切换语言失败: $err',
snackPosition: SnackPosition.BOTTOM,
);
}
}
}

View File

@ -1,115 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/localization/kr_language_utils.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import '../controllers/kr_language_selector_controller.dart';
class KRLanguageSelectorView extends GetView<KRLanguageSelectorController> {
const KRLanguageSelectorView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: Theme.of(context).primaryColor,
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color.fromRGBO(23, 151, 255, 0.15), //
Color.fromRGBO(23, 151, 255, 0.05), //
//
],
stops: [0.0, 0.28], //
),
),
child: Column(
children: [
AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 20.r,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
onPressed: () => Get.back(),
),
title: Text(
AppTranslations.kr_setting.switchLanguage,
style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
centerTitle: true,
),
Expanded(
child: Obx(
() => ListView.separated(
padding: EdgeInsets.all(16.r),
itemCount: controller.kr_languages.length,
separatorBuilder: (context, index) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
final language = controller.kr_languages[index];
return _kr_buildLanguageCard(language, context);
},
),
),
),
],
),
),
);
}
//
Widget _kr_buildLanguageCard(KRLanguage language, BuildContext context) {
return InkWell(
onTap: () => controller.kr_selectLanguage(language),
child: Container(
padding: EdgeInsets.all(16.r),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12.r),
),
child: Row(
children: [
//
CircleAvatar(
radius: 16.r,
backgroundColor: Colors.blue.withOpacity(0.1),
child: Text(
language.flagEmoji,
style: TextStyle(fontSize: 16),
),
),
SizedBox(width: 12.w),
//
Text(
language.languageName,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
const Spacer(),
//
if (controller.kr_selectedLanguage.value == language.countryCode)
Icon(
Icons.check_circle,
color: Colors.blue,
size: 20.r,
),
],
),
),
);
}
}

View File

@ -80,6 +80,7 @@ class KRLoginController extends GetxController
/// ///
var _countdown = 60; // var _countdown = 60; //
DateTime? _endTime; //
late Timer _timer; late Timer _timer;
var kr_countdownText = AppTranslations.kr_login.sendCode.obs; var kr_countdownText = AppTranslations.kr_login.sendCode.obs;
@ -294,6 +295,11 @@ class KRLoginController extends GetxController
return; return;
} }
if (!validateEmail(accountController.text.trim())) {
KRCommonUtil.kr_showToast('请输入有效的邮箱地址');
return;
}
if (psdController.text.isEmpty) { if (psdController.text.isEmpty) {
KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterPassword); KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterPassword);
return; return;
@ -306,7 +312,12 @@ class KRLoginController extends GetxController
/// ///
void kr_sendCode() async { void kr_sendCode() async {
if (accountController.text.isEmpty) { if (accountController.text.isEmpty) {
KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterAccount); KRCommonUtil.kr_showToast('请输入邮箱');
return;
}
if (!validateEmail(accountController.text.trim())) {
KRCommonUtil.kr_showToast('请输入有效的邮箱地址');
return; return;
} }
@ -356,8 +367,14 @@ class KRLoginController extends GetxController
/// ///
void kr_register() async { void kr_register() async {
// //
if (accountController.text.isEmpty) { final email = accountController.text.trim();
KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterAccount); if (email.isEmpty) {
KRCommonUtil.kr_showToast('请输入邮箱');
return;
}
if (!validateEmail(email)) {
KRCommonUtil.kr_showToast('请输入有效的邮箱地址');
return; return;
} }
@ -509,21 +526,31 @@ class KRLoginController extends GetxController
/// ///
void _startCountdown() { void _startCountdown() {
kr_canSendCode.value = false; kr_canSendCode.value = false;
_endTime = DateTime.now().add(const Duration(seconds: 60));
_timer = Timer.periodic(Duration(seconds: 1), (timer) { _timer.cancel();
if (_countdown > 0) { _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
_countdown -= 1; final now = DateTime.now();
kr_countdownText.value = "${_countdown}s"; if (_endTime != null && _endTime!.isAfter(now)) {
final remaining = _endTime!.difference(now).inSeconds;
_countdown = remaining;
kr_countdownText.value = "${remaining}s";
} else { } else {
kr_canSendCode.value = true; kr_canSendCode.value = true;
kr_countdownText.value = AppTranslations.kr_login.sendCode; kr_countdownText.value = AppTranslations.kr_login.sendCode;
_countdown = 60; _countdown = 60;
_endTime = null;
timer.cancel(); timer.cancel();
_onCountdownFinished(); _onCountdownFinished();
} }
}); });
} }
///
bool validateEmail(String str) {
return RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$').hasMatch(str);
}
/// ///
void _saveLoginData(String token) { void _saveLoginData(String token) {
KRAppRunData.getInstance().kr_saveUserInfo( KRAppRunData.getInstance().kr_saveUserInfo(

View File

@ -1,24 +0,0 @@
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/model/kr_area_code.dart'; // KRAreaCode
class KRSearchAreaController extends GetxController {
final areas = <KRAreaCodeItem>[].obs;
final searchQuery = ''.obs;
@override
void onInit() {
super.onInit();
areas.assignAll(KRAreaCode.kr_getCodeList());
}
List<KRAreaCodeItem> get filteredAreas {
if (searchQuery.value.isEmpty) {
return areas;
} else {
return areas
.where((area) => area.kr_dialCode.toLowerCase().contains(searchQuery.value.toLowerCase()))
.toList();
}
}
}

View File

@ -117,46 +117,6 @@ class KRLoginView extends GetView<KRLoginController> {
); );
} }
Widget _buildContentByEntry() {
final entry = (Get.arguments as Map<String, dynamic>?)?['entry'];
if (entry == 'forget_psd') {
return _buildForgetPasswordLayout();
} else if (entry == 'bind_email') {
return _buildBindEmailLayout();
} else if (entry == 'login') {
return _buildLoginEmailLayout();
}
return _buildForgetPasswordLayout(); //
}
Widget _buildForgetPasswordLayout() {
return Column(
children: [
Container(
constraints: BoxConstraints(minHeight: 300.w),
child: Column(
children: [
_buildStandardInputField(
controller: controller.psdController,
hintText: '新密码',
isPassword: true,
),
SizedBox(height: 10.w),
_buildStandardInputField(
controller: controller.agPsdController,
hintText: '确认密码',
isPassword: true,
),
],
),
),
SizedBox(height: 30.h),
_buildSaveButton(),
],
);
}
Widget _buildBindEmailLayout() { Widget _buildBindEmailLayout() {
return Column( return Column(
children: [ children: [
@ -192,33 +152,6 @@ class KRLoginView extends GetView<KRLoginController> {
); );
} }
Widget _buildLoginEmailLayout() {
return Column(
children: [
Container(
constraints: BoxConstraints(minHeight: 300.w),
child: Column(
children: [
_buildStandardInputField(
controller: controller.accountController,
hintText: 'Email',
),
SizedBox(height: 10),
_buildStandardInputField(
controller: controller.psdController,
hintText: '密码',
isPassword: true,
),
SizedBox(height: 10.w),
],
),
),
SizedBox(height: 30.h),
_buildSaveButton(),
],
);
}
/// ///
Widget _buildStandardInputField({ Widget _buildStandardInputField({
required TextEditingController controller, required TextEditingController controller,
@ -283,14 +216,6 @@ class KRLoginView extends GetView<KRLoginController> {
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [FilteringTextInputFormatter.digitsOnly],
onChanged: (value) { onChanged: (value) {
var v = value.replaceAll(RegExp("\\s+"), ""); var v = value.replaceAll(RegExp("\\s+"), "");
if (v.length % 2 == 0 && v.isNotEmpty) {
final half = v.length ~/ 2;
final first = v.substring(0, half);
final second = v.substring(half);
if (first == second) {
v = first;
}
}
const maxLen = 6; const maxLen = 6;
if (v.length > maxLen) { if (v.length > maxLen) {
v = v.substring(0, maxLen); v = v.substring(0, maxLen);

View File

@ -1,131 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/model/kr_area_code.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import '../controllers/kr_search_area_controller.dart';
class KRSearchAreaView extends GetView<KRSearchAreaController> {
final Function(KRAreaCodeItem, int) onSelect;
const KRSearchAreaView({super.key, required this.onSelect});
static void show(Function(KRAreaCodeItem, int) onSelect) {
Get.dialog(
KRSearchAreaView(onSelect: onSelect),
barrierDismissible: true,
);
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context); //
Get.lazyPut<KRSearchAreaController>(
() => KRSearchAreaController(),
);
return GestureDetector(
onTap: () => Get.back(), //
child: Scaffold(
backgroundColor: Colors.black.withOpacity(0.0),
body: Center(
child: GestureDetector(
onTap: () {}, //
child: Container(
width: 300.w,
height: 450.h,
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: theme.primaryColor,
borderRadius: BorderRadius.circular(15.w),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'login.selectOtherRegion'.tr,
style: KrAppTextStyle(
fontSize: 15.w,
fontWeight: FontWeight.w500,
color: theme.textTheme.titleMedium?.color),
),
SizedBox(height: 10.h),
TextField(
onChanged: (value) => controller.searchQuery.value = value,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search, color: Colors.grey),
hintText: 'login.search'.tr,
hintStyle: TextStyle(color: Colors.grey),
filled: true,
// fillColor: Colors.grey.shade200,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.w),
borderSide: BorderSide.none,
),
contentPadding: EdgeInsets.symmetric(vertical: 10.h),
),
style: theme.textTheme.bodyMedium?.copyWith(fontSize: 14.sp, fontFamily: 'AlibabaPuHuiTi-Regular',),
),
// SizedBox(height: 5.h),
Obx(() => Expanded(
child: ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: controller.filteredAreas.length,
itemBuilder: (context, index) {
final area = controller.filteredAreas[index];
return GestureDetector(
onTap: () {
onSelect(area, index); //
Get.back();
},
child: Container(
padding: EdgeInsets.symmetric(
vertical: 10.h, horizontal: 0),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey.shade300,
width: 0.2,
),
),
),
child: Row(
children: [
Text(area.kr_icon,
style: TextStyle(fontSize: 20.w)),
SizedBox(width: 12.w),
Expanded(
child: Text(
area.kr_name,
style: KrAppTextStyle(
fontSize: 13.w,
fontWeight: FontWeight.w500),
),
),
Text(
"+" + area.kr_dialCode,
style: KrAppTextStyle(
fontSize: 13.w,
fontWeight: FontWeight.w500),
),
],
),
),
);
},
),
)),
],
),
),
),
),
),
);
}
}

View File

@ -246,13 +246,13 @@ class KRPurchaseMembershipController extends GetxController {
/// ///
Future<void> kr_getAlreadySubscribe() async { Future<void> kr_getAlreadySubscribe() async {
final either = await _kr_subscribeApi.kr_getAlreadySubscribe(); final either = await _kr_subscribeApi.kr_getAlreadySubscribe(includeExpired: 'all');
either.fold( either.fold(
(error) => KRCommonUtil.kr_showToast(error.msg), (error) => KRCommonUtil.kr_showToast(error.msg),
(alreadySubscribe) { (alreadySubscribe) {
_kr_alreadySubscribe = alreadySubscribe; _kr_alreadySubscribe = alreadySubscribe;
KRLogUtil.kr_i( KRLogUtil.kr_i(
'订阅套餐: ${_kr_alreadySubscribe.map((e) => e.subscribeId).toList()}', '获取所有订阅记录(含过期): ${_kr_alreadySubscribe.map((e) => "ID:${e.userSubscribeId}, SubID:${e.subscribeId}").toList()}',
tag: 'PurchaseMembershipController'); tag: 'PurchaseMembershipController');
}, },
); );
@ -558,19 +558,18 @@ class KRPurchaseMembershipController extends GetxController {
// ========================================================================= // =========================================================================
final quantity = kr_getSelectedQuantity(); final quantity = kr_getSelectedQuantity();
// // ID
final isRenewal = _kr_alreadySubscribe final matchingSubscribes = _kr_alreadySubscribe
.any((subscribe) => subscribe.subscribeId == selectedPlan.kr_id); .where((subscribe) => subscribe.subscribeId == selectedPlan.kr_id)
final subscribeId = isRenewal .toList();
? _kr_alreadySubscribe
.firstWhere( final bool isRenewal = matchingSubscribes.isNotEmpty;
(subscribe) => subscribe.subscribeId == selectedPlan.kr_id, final int subscribeId = isRenewal
orElse: () => ? matchingSubscribes.last.userSubscribeId
KRAlreadySubscribe(userSubscribeId: 0, subscribeId: 0), //
)
.userSubscribeId
: 0; : 0;
print('📊 [Purchase] 订阅判断: isRenewal=$isRenewal, userSubscribeId=$subscribeId');
// //
final purchaseEither = isRenewal final purchaseEither = isRenewal
? await _kr_subscribeApi.kr_renewal( ? await _kr_subscribeApi.kr_renewal(

View File

@ -1,96 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/model/response/kr_package_list.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
///
class KRPlanDetailsDialog extends StatelessWidget {
final List<KRFeature> kr_features;
const KRPlanDetailsDialog({
Key? key,
required this.kr_features,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppTranslations.kr_purchaseMembership.planDetails,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Get.back(),
),
],
),
const SizedBox(height: 16),
Flexible(
child: ListView.builder(
shrinkWrap: true,
itemCount: kr_features.length,
itemBuilder: (context, index) {
final feature = kr_features[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
feature.kr_label,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
...feature.kr_details.map((detail) => Padding(
padding: const EdgeInsets.only(left: 16, bottom: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(
Icons.check_circle_outline,
size: 16,
color: Colors.green,
),
const SizedBox(width: 8),
Expanded(
child: Text(
detail.kr_description,
style: const TextStyle(
fontSize: 14,
color: Colors.black87,
),
),
),
],
),
)),
if (index < kr_features.length - 1)
const Divider(height: 24),
],
);
},
),
),
],
),
),
);
}
}

View File

@ -1,12 +0,0 @@
import 'package:get/get.dart';
import '../controllers/kr_setting_controller.dart';
class KRSettingBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<KRSettingController>(
() => KRSettingController(),
);
}
}

View File

@ -1,170 +0,0 @@
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/localization/kr_language_utils.dart';
import 'package:kaer_with_panels/app/routes/app_pages.dart';
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
import 'package:kaer_with_panels/app/utils/kr_country_util.dart';
import '../../../localization/app_translations.dart';
import '../../../themes/kr_theme_service.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:kaer_with_panels/app/common/app_run_data.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
class KRSettingController extends GetxController {
// AppTranslationsSetting
final AppTranslationsSetting kr_appTranslationsSetting =
AppTranslationsSetting();
//
final RxString kr_currentCountry = ''.obs;
//
final RxBool kr_autoConnect = true.obs;
//
final RxBool kr_notification = true.obs;
//
final RxBool kr_helpImprove = true.obs;
//
final RxString kr_version = ''.obs;
// IOS评分
final String kr_iosRating = '';
//
final RxString kr_language = ''.obs;
//
final RxString kr_themeOption = ''.obs;
final RxString kr_vpnMode = ''.obs;
final RxString kr_vpnModeRemark = ''.obs;
// VPN
void kr_changeVPNMode(String mode) {
KRLogUtil.kr_i('设置的VPN模式文本: ${kr_vpnMode.value}', tag: 'SettingController');
}
//
void kr_changeLanguage() {
Get.toNamed(Routes.KR_LANGUAGE_SELECTOR);
}
//
void kr_deleteAccount() {
//
if (!KRAppRunData.getInstance().kr_isLogin.value) {
//
// Get.toNamed(Routes.MR_LOGIN);
return;
}
//
Get.toNamed(Routes.KR_DELETE_ACCOUNT);
}
@override
void onInit() {
super.onInit();
_loadThemeOption();
kr_language.value = KRLanguageUtils.getCurrentLanguage().languageName;
//
ever(KRLanguageUtils.kr_language, (_) {
kr_language.value = KRLanguageUtils.kr_language.value;
_loadThemeOption();
kr_currentCountry.value = "";
kr_currentCountry.value = KRCountryUtil.kr_getCurrentCountryName();
kr_vpnMode.value = '';
kr_vpnMode.value =
kr_getConnectionTypeString(KRSingBoxImp().kr_connectionType.value);
kr_vpnModeRemark.value = '';
kr_vpnModeRemark.value = kr_getConnectionTypeRemark(KRSingBoxImp().kr_connectionType.value);
});
ever(KRCountryUtil.kr_currentCountry, (_) {
kr_currentCountry.value = KRCountryUtil.kr_getCurrentCountryName();
});
kr_currentCountry.value = KRCountryUtil.kr_getCurrentCountryName();
kr_vpnMode.value =
kr_getConnectionTypeString(KRSingBoxImp().kr_connectionType.value);
kr_vpnModeRemark.value = kr_getConnectionTypeRemark(KRSingBoxImp().kr_connectionType.value);
_kr_getVersion();
}
String kr_getConnectionTypeString(KRConnectionType type) {
switch (type) {
case KRConnectionType.global:
return AppTranslations.kr_setting.connectionTypeGlobal;
case KRConnectionType.rule:
return AppTranslations.kr_setting.connectionTypeRule;
// case KRConnectionType.direct:
// return AppTranslations.kr_setting.connectionTypeDirect;
}
}
String kr_getConnectionTypeRemark(KRConnectionType type) {
switch (type) {
case KRConnectionType.global:
return AppTranslations.kr_setting.connectionTypeGlobalRemark;
case KRConnectionType.rule:
return AppTranslations.kr_setting.connectionTypeRuleRemark;
// case KRConnectionType.direct:
// return AppTranslations.kr_setting.connectionTypeDirectRemark;
}
}
void _loadThemeOption() async {
final KRThemeService themeService = KRThemeService();
await themeService.init();
switch (themeService.kr_Theme) {
case ThemeMode.system:
kr_themeOption.value = AppTranslations.kr_setting.system;
break;
case ThemeMode.light:
kr_themeOption.value = AppTranslations.kr_setting.light;
break;
case ThemeMode.dark:
kr_themeOption.value = AppTranslations.kr_setting.dark;
break;
}
}
final count = 0.obs;
@override
void onReady() {
super.onReady();
}
@override
void onClose() {
super.onClose();
}
void increment() => count.value++;
void kr_updateConnectionType(KRConnectionType newType) {
if (KRSingBoxImp().kr_connectionType.value != newType) {
KRLogUtil.kr_i('更新连接类型: $newType', tag: 'SettingController');
KRSingBoxImp().kr_updateConnectionType(newType);
kr_vpnMode.value = kr_getConnectionTypeString(newType);
kr_vpnModeRemark.value = kr_getConnectionTypeRemark(newType);
//
}
}
//
Future<void> _kr_getVersion() async {
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
kr_version.value = packageInfo.version;
}
}

View File

@ -1,493 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import '../../../services/singbox_imp/kr_sing_box_imp.dart';
import '../controllers/kr_setting_controller.dart';
import '../../../themes/kr_theme_service.dart';
import '../../../localization/app_translations.dart';
import '../../../routes/app_pages.dart';
import '../../../common/app_run_data.dart';
class KRSettingView extends GetView<KRSettingController> {
const KRSettingView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: Theme.of(context).primaryColor,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 20.r,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
onPressed: () => Get.back(),
),
centerTitle: true,
title: Text(
AppTranslations.kr_setting.title,
style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
body: Obx(() {
return Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color.fromRGBO(23, 151, 255, 0.15),
Color.fromRGBO(23, 151, 255, 0.05),
],
stops: [0.0, 0.3],
),
),
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: kToolbarHeight + 20.w),
_kr_buildSectionTitle(
context, AppTranslations.kr_setting.vpnConnection),
_kr_buildVPNSection(context),
_kr_buildSectionTitle(
context, AppTranslations.kr_setting.general),
_kr_buildGeneralSection(context),
SizedBox(height: 100.h),
],
),
),
),
);
}),
);
}
Widget _kr_buildSectionTitle(BuildContext context, String title) {
return Padding(
padding: EdgeInsets.fromLTRB(16.w, 24.h, 16.w, 8.h),
child: Text(
title,
style: KrAppTextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
);
}
Widget _kr_buildVPNSection(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16.w),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12.r),
),
child: Column(
children: [
_kr_buildSelectionTile(
context,
title: AppTranslations.kr_setting.mode,
value: controller.kr_vpnMode.value,
// subtitle: controller.kr_vpnModeRemark.value,
onTap: () => _kr_showRouteRuleSelectionSheet(context),
),
_kr_buildDivider(),
// _kr_buildSwitchTile(
// context,
// title: AppTranslations.kr_setting.autoConnect,
// value: controller.kr_autoConnect,
// onChanged: (value) => controller.kr_autoConnect.value = value,
// ),
// _kr_buildDivider(),
// _kr_buildSelectionTile(
// context,
// title: AppTranslations.kr_setting.routeRule,
// value: controller.kr_routeRule.value,
// onTap: () => _kr_showRouteRuleSelectionSheet(context),
// ),
// _kr_buildDivider(),
_kr_buildSelectionTile(
context,
title: AppTranslations.kr_setting.countrySelector,
subtitle: AppTranslations.kr_setting.connectionTypeRuleRemark,
value: controller.kr_currentCountry.value,
onTap: () => Get.toNamed(Routes.KR_COUNTRY_SELECTOR),
),
],
),
);
}
void _kr_showVPNModeSelectionSheet(BuildContext context) {
Get.bottomSheet(
Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(16.r)),
),
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 16.r),
child: Wrap(
children: [
ListTile(
title: Center(
child: Text(AppTranslations.kr_setting.vpnModeSmart),
),
onTap: () {
controller.kr_changeVPNMode(AppTranslations.kr_setting.vpnModeSmart);
Get.back();
},
),
ListTile(
title: Center(
child: Text(AppTranslations.kr_setting.vpnModeSecure),
),
onTap: () {
controller.kr_changeVPNMode(AppTranslations.kr_setting.vpnModeSecure);
Get.back();
},
),
],
),
),
),
);
}
void _kr_showRouteRuleSelectionSheet(BuildContext context) {
Get.bottomSheet(
Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(16.r)),
),
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 16.r),
child: Wrap(
children: KRConnectionType.values.map((type) {
return ListTile(
title: Center(
child: Text(
controller.kr_getConnectionTypeString(type),
style: KrAppTextStyle(
fontSize: 16,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
),
onTap: () {
controller.kr_updateConnectionType(type);
Get.back();
},
);
}).toList(),
),
),
),
);
}
Widget _kr_buildGeneralSection(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16.w),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12.r),
),
child: Column(
children: [
Obx(() => _kr_buildSelectionTile(
context,
title: AppTranslations.kr_setting.appearance,
value: controller.kr_themeOption.value,
onTap: () => _showThemeSelectionSheet(context),
)),
_kr_buildDivider(),
_kr_buildSwitchTile(
context,
title: AppTranslations.kr_setting.notifications,
value: controller.kr_notification,
onChanged: (value) => controller.kr_notification.value = value,
),
_kr_buildDivider(),
_kr_buildSwitchTile(
context,
title: AppTranslations.kr_setting.helpImprove,
value: controller.kr_helpImprove,
onChanged: (value) => controller.kr_helpImprove.value = value,
),
_kr_buildDivider(),
Obx(() {
final appRunData = KRAppRunData.getInstance();
final isLoggedIn = appRunData.kr_isLogin.value;
final isDeviceLogin = appRunData.isDeviceLogin();
if (!isLoggedIn) {
//
return SizedBox.shrink();
} else if (isDeviceLogin) {
// "点击这里登录/注册"
return _kr_buildActionTile(
context,
title: 'userInfo.loginRegister'.tr,
trailing: "",
onTap: () => Get.toNamed(Routes.MR_LOGIN),
);
} else {
//
final userEmail = appRunData.kr_account.value ?? AppTranslations.kr_userInfo.myAccount;
return _kr_buildActionTile(
context,
title: userEmail,
trailing: AppTranslations.kr_setting.goToDelete,
onTap: controller.kr_deleteAccount,
);
}
}),
_kr_buildDivider(),
// _kr_buildTitleTile(
// context,
// title: AppTranslations.kr_setting.rateUs,
// ),
// _kr_buildDivider(),
Obx(() => _kr_buildValueTile(
context,
title: AppTranslations.kr_setting.version,
value: controller.kr_version.value,
)),
_kr_buildDivider(),
_kr_buildSelectionTile(
context,
title: AppTranslations.kr_setting.switchLanguage,
value: controller.kr_language.value,
onTap: controller.kr_changeLanguage,
),
],
),
);
}
void _showThemeSelectionSheet(BuildContext context) {
Get.bottomSheet(
Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(16.r)),
),
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 16.r),
child: Wrap(
children: ThemeMode.values.map((option) {
String optionText;
switch (option) {
case ThemeMode.system:
optionText = AppTranslations.kr_setting.system;
break;
case ThemeMode.light:
optionText = AppTranslations.kr_setting.light;
break;
case ThemeMode.dark:
optionText = AppTranslations.kr_setting.dark;
break;
}
return ListTile(
title: Center(
child: Text(
optionText,
style: KrAppTextStyle(
fontSize: 16,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
),
onTap: () async {
final KRThemeService themeService = KRThemeService();
await themeService.kr_switchTheme(option);
controller.kr_themeOption.value = optionText;
Get.back();
},
);
}).toList(),
),
),
),
);
}
Widget _kr_buildSelectionTile(
BuildContext context, {
required String title,
required String value,
String? subtitle,
required VoidCallback onTap,
}) {
return ListTile(
title: Text(
title,
style: KrAppTextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
subtitle: subtitle != null
? Text(
subtitle,
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
)
: null,
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
value,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
SizedBox(width: 4.w),
Icon(
Icons.arrow_forward_ios,
size: 16.r,
color: Theme.of(context).textTheme.bodySmall?.color,
),
],
),
onTap: onTap,
);
}
Widget _kr_buildSwitchTile(
BuildContext context, {
required String title,
String? subtitle,
required RxBool value,
required Function(bool) onChanged,
}) {
return ListTile(
title: Text(
title,
style: KrAppTextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
subtitle: subtitle != null
? Text(
subtitle,
style: KrAppTextStyle(
fontSize: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
)
: null,
trailing: Obx(
() => CupertinoSwitch(
value: value.value,
onChanged: onChanged,
activeColor: Colors.blue,
),
),
);
}
Widget _kr_buildActionTile(
BuildContext context, {
required String title,
required String trailing,
required VoidCallback onTap,
}) {
return ListTile(
title: Text(
title,
style: KrAppTextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
trailing: Text(
trailing,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
onTap: onTap,
);
}
Widget _kr_buildTitleTile(
BuildContext context, {
required String title,
}) {
return ListTile(
title: Text(
title,
style: KrAppTextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
);
}
Widget _kr_buildValueTile(
BuildContext context, {
required String title,
required String value,
}) {
return ListTile(
title: Text(
title,
style: KrAppTextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
trailing: Text(
value,
style: KrAppTextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.bodySmall?.color,
),
),
);
}
Widget _kr_buildDivider() {
return Divider(
height: 1.h,
thickness: 0.2,
color: const Color(0xFFEEEEEE),
);
}
}

View File

@ -1,11 +0,0 @@
import 'package:get/get.dart';
import '../controllers/kr_webview_controller.dart';
class KRWebViewBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<KRWebViewController>(
() => KRWebViewController(),
);
}
}

View File

@ -1,192 +0,0 @@
import 'package:get/get.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:io' show Platform;
import 'package:kaer_with_panels/app/services/api_service/api.dart';
import 'package:kaer_with_panels/app/services/api_service/kr_web_api.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
/// WebView
/// WebView
class KRWebViewController extends GetxController {
//
final RxBool kr_isLoading = true.obs;
//
final RxString kr_title = ''.obs;
// WebView
late final WebViewController kr_webViewController;
// URL
static const String kr_defaultUrl = '';
final String kr_url = Get.arguments['url'] as String;
// Web API
final KRWebApi _kr_webApi = KRWebApi();
//
final RxBool kr_isHtml = false.obs;
final RxBool kr_isMarkdown = false.obs;
final RxString kr_content = ''.obs;
@override
void onInit() {
super.onInit();
// URL
if (kr_url.contains(Api.kr_getSiteTos) || kr_url.contains(Api.kr_getSitePrivacy)) {
//
if (kr_url.contains(Api.kr_getSiteTos)) {
kr_title.value = AppTranslations.kr_login.termsOfService;
} else {
kr_title.value = AppTranslations.kr_login.privacyPolicy;
}
kr_getWebText();
} else {
// WebView
kr_initWebView();
}
}
/// WebView
void kr_initWebView() {
kr_webViewController = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onNavigationRequest: (NavigationRequest request) async {
//
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
KRLogUtil.kr_i('处理支付链接: ${request.url}', tag: 'WebViewController');
//
if (await kr_handleUrlLaunch(request.url)) {
return NavigationDecision.prevent;
}
}
return NavigationDecision.navigate;
},
onPageStarted: kr_handlePageStarted,
onPageFinished: kr_handlePageFinished,
),
);
//
if (kr_url.contains(Api.kr_getSiteTos) || kr_url.contains(Api.kr_getSitePrivacy)) {
kr_getWebText();
} else {
kr_webViewController.loadRequest(Uri.parse(kr_url));
}
}
/// WebView
Future<void> kr_getWebText() async {
try {
final response = await _kr_webApi.kr_getWebText(kr_url);
response.fold(
(error) async {
KRLogUtil.kr_e('获取网页内容失败: $error', tag: 'WebViewController');
//
kr_content.value = 'Failed to load, please try again later';
kr_isLoading.value = false;
},
(content) async {
KRLogUtil.kr_i('获取到内容: $content', tag: 'WebViewController');
// Markdown
kr_isMarkdown.value = content.contains('**') ||
content.contains('*') ||
content.contains('#') ||
content.contains('- ') ||
content.contains('[');
kr_isHtml.value = !kr_isMarkdown.value && content.contains('<') && content.contains('>');
KRLogUtil.kr_i('内容类型 - Markdown: ${kr_isMarkdown.value}, HTML: ${kr_isHtml.value}', tag: 'WebViewController');
if (kr_isMarkdown.value) {
// Markdown 使
kr_content.value = content;
} else if (kr_isHtml.value) {
// HTML 使
kr_content.value = content;
} else {
// 使
kr_content.value = content;
}
kr_isLoading.value = false;
},
);
} catch (e) {
KRLogUtil.kr_e('获取网页内容出错: $e', tag: 'WebViewController');
kr_content.value = 'Loading error, please try again later';
kr_isLoading.value = false;
}
}
///
void kr_handlePageStarted(String url) {
kr_isLoading.value = true;
}
///
void kr_handlePageFinished(String url) async {
kr_isLoading.value = false;
await kr_updateTitle();
}
///
Future<void> kr_updateTitle() async {
final String? kr_pageTitle = await kr_webViewController.getTitle();
kr_title.value = kr_pageTitle ?? '';
}
///
Future<void> kr_reloadPage() async {
await kr_webViewController.reload();
}
/// URL
Future<void> kr_loadUrl(String url) async {
await kr_webViewController.loadRequest(Uri.parse(url));
}
/// URL启动
Future<bool> kr_handleUrlLaunch(String url) async {
try {
KRLogUtil.kr_i('正在处理URL跳转: $url', tag: 'WebViewController');
final uri = Uri.parse(url);
//
if (uri.scheme == 'alipays' ||
uri.scheme == 'alipay' ||
uri.scheme == 'weixin' ||
uri.scheme == 'wx') {
KRLogUtil.kr_i('检测到支付应用scheme: ${uri.scheme}', tag: 'WebViewController');
//
if (await canLaunchUrl(uri)) {
return await launchUrl(
uri,
mode: LaunchMode.externalApplication,
);
}
// 使
final httpUri = Uri.parse('https://${uri.host}${uri.path}?${uri.query}');
KRLogUtil.kr_i('尝试使用浏览器打开: $httpUri', tag: 'WebViewController');
if (await canLaunchUrl(httpUri)) {
return await launchUrl(
httpUri,
mode: LaunchMode.externalApplication,
);
}
KRLogUtil.kr_e('无法启动URL: $url', tag: 'WebViewController');
}
return false;
} catch (e) {
KRLogUtil.kr_e('URL跳转错误: $e', tag: 'WebViewController');
return false;
}
}
}

View File

@ -1,177 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import '../../../widgets/kr_app_text_style.dart';
import '../controllers/kr_webview_controller.dart';
import '../../../services/api_service/api.dart';
import '../../../utils/kr_log_util.dart';
/// WebView
class KRWebView extends GetView<KRWebViewController> {
const KRWebView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 20.sp,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
onPressed: () => Get.back(),
),
centerTitle: true,
title: Text(
controller.kr_title.value,
style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
body: _buildBody(),
);
}
///
Widget _buildBody() {
return Stack(
children: [
_buildContent(),
_buildLoadingIndicator(),
],
);
}
///
Widget _buildContent() {
if (controller.kr_url.contains(Api.kr_getSiteTos) ||
controller.kr_url.contains(Api.kr_getSitePrivacy)) {
return _buildProtocolContent();
} else {
return _buildWebView();
}
}
///
Widget _buildProtocolContent() {
return Obx(() {
if (controller.kr_isHtml.value) {
return SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Html(
data: controller.kr_content.value,
style: {
'body': Style(
margin: Margins.all(0),
padding: HtmlPaddings.all(0),
fontSize: FontSize(14.sp),
color: Theme.of(Get.context!).textTheme.bodySmall?.color,
fontFamily: 'AlibabaPuHuiTi-Regular',
lineHeight: LineHeight(1.4),
),
'p': Style(
margin: Margins.only(bottom: 8.h),
),
'b': Style(
fontWeight: FontWeight.bold,
),
'i': Style(
fontStyle: FontStyle.italic,
),
'a': Style(
color: Colors.blue,
textDecoration: TextDecoration.underline,
),
},
shrinkWrap: true,
),
);
} else if (controller.kr_isMarkdown.value) {
return SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: MarkdownBody(
data: controller.kr_content.value,
styleSheet: MarkdownStyleSheet(
p: TextStyle(
fontSize: 14.sp,
color: Theme.of(Get.context!).textTheme.bodySmall?.color,
fontFamily: 'AlibabaPuHuiTi-Regular',
height: 1.4,
),
strong: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.bold,
color: Theme.of(Get.context!).textTheme.bodySmall?.color,
fontFamily: 'AlibabaPuHuiTi-Regular',
height: 1.4,
),
em: TextStyle(
fontSize: 14.sp,
fontStyle: FontStyle.italic,
color: Theme.of(Get.context!).textTheme.bodySmall?.color,
fontFamily: 'AlibabaPuHuiTi-Regular',
height: 1.4,
),
a: TextStyle(
fontSize: 14.sp,
color: Colors.blue,
decoration: TextDecoration.underline,
fontFamily: 'AlibabaPuHuiTi-Regular',
height: 1.4,
),
),
),
);
} else {
return SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Text(
controller.kr_content.value,
style: TextStyle(
fontSize: 14.sp,
color: Theme.of(Get.context!).textTheme.bodySmall?.color,
fontFamily: 'AlibabaPuHuiTi-Regular',
height: 1.4,
),
),
);
}
});
}
/// WebView
Widget _buildWebView() {
return WebViewWidget(
controller: controller.kr_webViewController,
);
}
///
Widget _buildLoadingIndicator() {
return Obx(
() => controller.kr_isLoading.value
? const Center(
child: CircularProgressIndicator(),
)
: const SizedBox.shrink(),
);
}
///
void _showErrorSnackbar(String title, String message) {
Get.snackbar(
title,
message,
snackPosition: SnackPosition.BOTTOM,
);
}
}

View File

@ -14,6 +14,7 @@ import 'package:kaer_with_panels/app/localization/kr_language_utils.dart';
import 'package:kaer_with_panels/app/utils/kr_common_util.dart'; import 'package:kaer_with_panels/app/utils/kr_common_util.dart';
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart'; import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
import 'package:kaer_with_panels/app/services/kr_site_config_service.dart'; import 'package:kaer_with_panels/app/services/kr_site_config_service.dart';
import 'package:kaer_with_panels/app/utils/kr_http_adapter_util.dart';
import 'package:kaer_with_panels/singbox/model/singbox_status.dart'; import 'package:kaer_with_panels/singbox/model/singbox_status.dart';
// import 'package:crypto/crypto.dart'; // import 'package:crypto/crypto.dart';
@ -74,52 +75,11 @@ class HttpUtil {
}; };
// 🔧 HttpClientAdapter sing-box mixed // 🔧 HttpClientAdapter sing-box mixed
// 退 //
KRLogUtil.kr_i('🔧 配置 HttpClientAdapter...', tag: 'HttpUtil'); KRLogUtil.kr_i('🔧 配置 HttpClientAdapter...', tag: 'HttpUtil');
_dio.httpClientAdapter = IOHttpClientAdapter( _dio.httpClientAdapter = KRHttpAdapterUtil.createAdapter(
createHttpClient: () { useSingBoxProxy: true,
KRLogUtil.kr_i('📱 createHttpClient 回调被调用', tag: 'HttpUtil'); timeout: const Duration(seconds: 10),
final client = HttpClient();
// 退
client.findProxy = (url) {
try {
// SingBox
final singBoxStatus = KRSingBoxImp.instance.kr_status;
final isProxyAvailable = singBoxStatus == SingboxStatus.started();
if (!isProxyAvailable) {
// 使
KRLogUtil.kr_i(
'🔄 代理未运行,使用直连模式: $url',
tag: 'HttpUtil',
);
return 'DIRECT';
}
// 使
final proxyConfig = KRSingBoxImp.instance.kr_buildProxyRule();
KRLogUtil.kr_i(
'✅ 使用代理模式, url: $url, proxy: $proxyConfig',
tag: 'HttpUtil',
);
return proxyConfig;
} catch (e) {
// 退
KRLogUtil.kr_w(
'⚠️ 代理配置异常,回退到直连: $e',
tag: 'HttpUtil',
);
return 'DIRECT';
}
};
// 退
client.connectionTimeout = const Duration(seconds: 10);
client.badCertificateCallback = (cert, host, port) => true;
return client;
},
); );
KRLogUtil.kr_i('✅ HttpUtil.initDio() 初始化完成', tag: 'HttpUtil'); KRLogUtil.kr_i('✅ HttpUtil.initDio() 初始化完成', tag: 'HttpUtil');
} }

View File

@ -8,10 +8,6 @@ import 'package:kaer_with_panels/app/modules/hi_node_list/views/hi_page_node_vie
import 'package:kaer_with_panels/app/modules/hi_user_info/bindings/hi_user_info_binding.dart'; import 'package:kaer_with_panels/app/modules/hi_user_info/bindings/hi_user_info_binding.dart';
import 'package:kaer_with_panels/app/modules/hi_user_info/views/hi_user_info_view.dart'; import 'package:kaer_with_panels/app/modules/hi_user_info/views/hi_user_info_view.dart';
import '../modules/kr_language_selector/bindings/kr_language_selector_binding.dart';
import '../modules/kr_language_selector/views/kr_language_selector_view.dart';
import '../modules/kr_country_selector/bindings/kr_country_selector_binding.dart';
import '../modules/kr_country_selector/views/kr_country_selector_view.dart';
import '../modules/kr_crisp_chat/bindings/kr_crisp_binding.dart'; import '../modules/kr_crisp_chat/bindings/kr_crisp_binding.dart';
import '../modules/kr_crisp_chat/views/kr_crisp_view.dart'; import '../modules/kr_crisp_chat/views/kr_crisp_view.dart';
import '../modules/kr_delete_account/bindings/kr_delete_account_binding.dart'; import '../modules/kr_delete_account/bindings/kr_delete_account_binding.dart';
@ -26,16 +22,10 @@ import '../modules/kr_message/bindings/kr_message_binding.dart';
import '../modules/kr_message/views/kr_message_view.dart'; import '../modules/kr_message/views/kr_message_view.dart';
import '../modules/kr_purchase_membership/bindings/kr_purchase_membership_binding.dart'; import '../modules/kr_purchase_membership/bindings/kr_purchase_membership_binding.dart';
import '../modules/kr_purchase_membership/views/kr_purchase_membership_view.dart'; import '../modules/kr_purchase_membership/views/kr_purchase_membership_view.dart';
import '../modules/kr_setting/bindings/kr_setting_binding.dart';
import '../modules/kr_setting/views/kr_setting_view.dart';
import '../modules/kr_webview/bindings/kr_webview_binding.dart';
import '../modules/kr_webview/views/kr_webview_view.dart';
import '../modules/kr_order_status/bindings/kr_order_status_binding.dart'; import '../modules/kr_order_status/bindings/kr_order_status_binding.dart';
import '../modules/kr_order_status/views/kr_order_status_view.dart'; import '../modules/kr_order_status/views/kr_order_status_view.dart';
import '../modules/kr_splash/bindings/kr_splash_binding.dart'; import '../modules/kr_splash/bindings/kr_splash_binding.dart';
import '../modules/kr_splash/views/kr_splash_view.dart'; import '../modules/kr_splash/views/kr_splash_view.dart';
import '../modules/kr_device_management/bindings/kr_device_management_binding.dart';
import '../modules/kr_device_management/views/kr_device_management_view.dart';
import 'package:kaer_with_panels/app/routes/transitions/slide_transparent_transition.dart'; import 'package:kaer_with_panels/app/routes/transitions/slide_transparent_transition.dart';
import 'package:kaer_with_panels/app/widgets/swipe/swipe_wrapper.dart'; import 'package:kaer_with_panels/app/widgets/swipe/swipe_wrapper.dart';
@ -84,30 +74,12 @@ class AppPages {
popGesture: false, popGesture: false,
arguments: {'showSubscriptionButton': true}, // arguments: {'showSubscriptionButton': true}, //
), ),
GetPage(
name: _Paths.KR_SETTING,
page: () => SwipeWrapper.detect(() => const KRSettingView()),
binding: KRSettingBinding(),
popGesture: false,
),
GetPage( GetPage(
name: _Paths.KR_INVITE, name: _Paths.KR_INVITE,
page: () => SwipeWrapper.detect(() => const KRInviteView()), page: () => SwipeWrapper.detect(() => const KRInviteView()),
binding: KRInviteBinding(), binding: KRInviteBinding(),
popGesture: false, popGesture: false,
), ),
GetPage(
name: _Paths.KR_LANGUAGE_SELECTOR,
page: () => SwipeWrapper.detect(() => const KRLanguageSelectorView()),
binding: KRLanguageSelectorBinding(),
popGesture: false,
),
GetPage(
name: _Paths.KR_COUNTRY_SELECTOR,
page: () => SwipeWrapper.detect(() => const KRCountrySelectorView()),
binding: KRCountrySelectorBinding(),
popGesture: false,
),
GetPage( GetPage(
name: _Paths.KR_PURCHASE_MEMBERSHIP, name: _Paths.KR_PURCHASE_MEMBERSHIP,
page: () => SwipeWrapper.detect(() => const KRPurchaseMembershipView()), page: () => SwipeWrapper.detect(() => const KRPurchaseMembershipView()),
@ -127,12 +99,6 @@ class AppPages {
binding: KrDeleteAccountBinding(), binding: KrDeleteAccountBinding(),
popGesture: false, popGesture: false,
), ),
GetPage(
name: Routes.KR_WEBVIEW,
page: () => SwipeWrapper.detect(() => const KRWebView()),
binding: KRWebViewBinding(),
popGesture: false,
),
GetPage( GetPage(
name: Routes.KR_ORDER_STATUS, name: Routes.KR_ORDER_STATUS,
page: () => SwipeWrapper.detect(() => const KROrderStatusView()), page: () => SwipeWrapper.detect(() => const KROrderStatusView()),
@ -145,12 +111,6 @@ class AppPages {
binding: KRCrispBinding(), binding: KRCrispBinding(),
popGesture: false, popGesture: false,
), ),
GetPage(
name: _Paths.KR_DEVICE_MANAGEMENT,
page: () => SwipeWrapper.detect(() => const KRDeviceManagementView()),
binding: KRDeviceManagementBinding(),
popGesture: false,
),
GetPage( GetPage(
name: _Paths.HI_NODE_LIST, name: _Paths.HI_NODE_LIST,
page: () => SwipeWrapper.detect(() => const HINodePageView()), page: () => SwipeWrapper.detect(() => const HINodePageView()),

View File

@ -17,7 +17,6 @@ abstract class Routes {
static const KR_PURCHASE_MEMBERSHIP = _Paths.KR_PURCHASE_MEMBERSHIP; static const KR_PURCHASE_MEMBERSHIP = _Paths.KR_PURCHASE_MEMBERSHIP;
static const KR_MESSAGE = _Paths.KR_MESSAGE; static const KR_MESSAGE = _Paths.KR_MESSAGE;
static const KR_DELETE_ACCOUNT = _Paths.KR_DELETE_ACCOUNT; static const KR_DELETE_ACCOUNT = _Paths.KR_DELETE_ACCOUNT;
static const KR_WEBVIEW = _Paths.KR_WEBVIEW;
static const KR_ORDER_STATUS = '/kr-order-status'; static const KR_ORDER_STATUS = '/kr-order-status';
static const KR_CRISP = _Paths.KR_CRISP; static const KR_CRISP = _Paths.KR_CRISP;
static const KR_DEVICE_MANAGEMENT = _Paths.KR_DEVICE_MANAGEMENT; static const KR_DEVICE_MANAGEMENT = _Paths.KR_DEVICE_MANAGEMENT;
@ -43,7 +42,6 @@ abstract class _Paths {
static const KR_PURCHASE_MEMBERSHIP = '/kr-purchase-membership'; static const KR_PURCHASE_MEMBERSHIP = '/kr-purchase-membership';
static const KR_MESSAGE = '/kr-message'; static const KR_MESSAGE = '/kr-message';
static const KR_DELETE_ACCOUNT = '/kr-delete-account'; static const KR_DELETE_ACCOUNT = '/kr-delete-account';
static const KR_WEBVIEW = '/kr_webview';
static const KR_CRISP = '/kr-crisp'; static const KR_CRISP = '/kr-crisp';
static const KR_DEVICE_MANAGEMENT = '/kr-device-management'; static const KR_DEVICE_MANAGEMENT = '/kr-device-management';
static const HI_MENU = '/hi_menu'; static const HI_MENU = '/hi_menu';

View File

@ -22,6 +22,7 @@ import '../kr_site_config_service.dart';
import '../../common/app_config.dart'; import '../../common/app_config.dart';
import 'package:dio/dio.dart' as dio; import 'package:dio/dio.dart' as dio;
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart'; import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
import 'package:kaer_with_panels/app/utils/kr_http_adapter_util.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
class KRAuthApi { class KRAuthApi {
@ -401,6 +402,13 @@ class KRAuthApi {
// 使 Dio // 使 Dio
final dioInstance = dio.Dio(); final dioInstance = dio.Dio();
// 使 Adapter SSL
dioInstance.httpClientAdapter = KRHttpAdapterUtil.createAdapter(
useSingBoxProxy: false, //
forceDirect: true,
timeout: const Duration(seconds: 10),
);
final baseUrl = AppConfig.getInstance().baseUrl; final baseUrl = AppConfig.getInstance().baseUrl;
final url = '$baseUrl${Api.kr_deviceLogin}'; final url = '$baseUrl${Api.kr_deviceLogin}';

View File

@ -229,8 +229,11 @@ class KRSubscribeApi {
/// ///
Future<Either<HttpError, List<KRAlreadySubscribe>>> Future<Either<HttpError, List<KRAlreadySubscribe>>>
kr_getAlreadySubscribe() async { kr_getAlreadySubscribe({String? includeExpired}) async {
final Map<String, dynamic> data = <String, dynamic>{}; final Map<String, dynamic> data = <String, dynamic>{};
if (includeExpired != null) {
data['includeExpired'] = includeExpired;
}
BaseResponse<KRAlreadySubscribeList> baseResponse = BaseResponse<KRAlreadySubscribeList> baseResponse =
await HttpUtil.getInstance().request<KRAlreadySubscribeList>( await HttpUtil.getInstance().request<KRAlreadySubscribeList>(

View File

@ -3,6 +3,8 @@ import 'package:get/get.dart';
import '../modules/kr_home/views/hi_subscription_corner_button.dart'; import '../modules/kr_home/views/hi_subscription_corner_button.dart';
import 'package:kaer_with_panels/main.dart'; import 'package:kaer_with_panels/main.dart';
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart'; import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:kaer_with_panels/app/routes/app_pages.dart';
class GlobalOverlayService extends GetxService { class GlobalOverlayService extends GetxService {
Color? _currentColor; Color? _currentColor;
@ -24,9 +26,14 @@ class GlobalOverlayService extends GetxService {
} }
OverlayEntry? _overlayEntry; OverlayEntry? _overlayEntry;
final RxBool _isVisible = false.obs; final RxBool _isVisible = false.obs;
final RxString _currentRoute = ''.obs;
bool get isVisible => _isVisible.value; bool get isVisible => _isVisible.value;
void updateCurrentRoute(String route) {
_currentRoute.value = route;
}
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@ -77,20 +84,56 @@ class GlobalOverlayService extends GetxService {
// 2 money-icon // 2 money-icon
Positioned( Positioned(
top: MediaQuery.of(context).padding.top + 8, top: MediaQuery.of(context).padding.top,
right: ((radius - (statusBarHeight / 2)) / 2) + 3, right: (radius / 2),
child: GestureDetector( child: FractionalTranslation(
behavior: HitTestBehavior.translucent, // // Offset x y
onTap: () { // 0.5 50%-0.5 50%
// translation: const Offset(0.5, 0),
print('🔥 money-icon tapped → trigger HISubscriptionCornerButton animation'); child: GestureDetector(
GlobalOverlayService.instance.triggerSubscriptionAnimation(); behavior: HitTestBehavior.translucent,
}, onTap: () {
child: IgnorePointer( print('🔥 money-icon tapped');
ignoring: false, // GlobalOverlayService.instance.triggerSubscriptionAnimation();
child: KrLocalImage( },
imageName: 'money-icon', child: IgnorePointer(
imageType: ImageType.svg, ignoring: false,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
KrLocalImage(
imageName: 'money-icon',
imageType: ImageType.svg,
width: 32.w, //
),
SizedBox(height: 2.w),
Obx(() {
final current = _currentRoute.value;
print('🔥 GlobalOverlayService Obx: currentRoute = $current');
// 1. 2. 3.
bool isHidden = _currentColor == Colors.transparent ||
current == Routes.KR_PURCHASE_MEMBERSHIP ||
current.contains(Routes.KR_PURCHASE_MEMBERSHIP) ||
Routes.KR_PURCHASE_MEMBERSHIP.contains(current) ||
current == Routes.KR_ORDER_STATUS ||
current.contains('purchase-membership');
if (isHidden) {
return const SizedBox.shrink();
}
return Text(
'购买套餐',
style: TextStyle(
color: Colors.black,
fontSize: 12.sp,
),
);
}),
],
),
), ),
), ),
), ),

View File

@ -225,17 +225,21 @@ class KRDeviceInfoService {
} }
/// Windows设备ID - 使GUID /// Windows设备ID - 使GUID
/// 🔧 使 flutter_udid wmic
Future<String> _getWindowsDeviceId() async { Future<String> _getWindowsDeviceId() async {
try { try {
final windowsInfo = await _deviceInfo.windowsInfo; final windowsInfo = await _deviceInfo.windowsInfo;
// 使 flutter_udid // 🔧 使 FlutterUdid.consistentUdid
String udid = await FlutterUdid.consistentUdid; // Windows "cmd.exe /c wmic csproduct get UUID"
// 使 CREATE_NO_WINDOW
// 使 device_info_plus
// windowsInfo.deviceId
// //
final factors = [ final factors = [
udid, windowsInfo.deviceId, // ID ( MachineGuid)
windowsInfo.deviceId, // ID
windowsInfo.computerName, // windowsInfo.computerName, //
windowsInfo.productName, // windowsInfo.productName, //
windowsInfo.numberOfCores.toString(), // CPU核心数 windowsInfo.numberOfCores.toString(), // CPU核心数

View File

@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
import '../model/response/kr_site_config.dart'; import '../model/response/kr_site_config.dart';
import '../common/app_config.dart'; import '../common/app_config.dart';
import '../utils/kr_log_util.dart'; import '../utils/kr_log_util.dart';
import '../utils/kr_http_adapter_util.dart';
import 'singbox_imp/kr_sing_box_imp.dart'; import 'singbox_imp/kr_sing_box_imp.dart';
/// ///
@ -25,6 +26,14 @@ class KRSiteConfigService extends ChangeNotifier {
'🌐 网站配置服务:使用直连模式(不通过代理)', '🌐 网站配置服务:使用直连模式(不通过代理)',
tag: 'KRSiteConfigService', tag: 'KRSiteConfigService',
); );
// 🔧 SSL
_dio.httpClientAdapter = KRHttpAdapterUtil.createAdapter(
useSingBoxProxy: false,
forceDirect: true,
timeout: const Duration(seconds: 20),
);
if (kDebugMode) { if (kDebugMode) {
print('🌐 网站配置服务:使用直连模式,避免 SingBox 未初始化问题'); print('🌐 网站配置服务:使用直连模式,避免 SingBox 未初始化问题');
} }

View File

@ -0,0 +1,47 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
class KRHttpAdapterUtil {
/// HttpClientAdapter
///
/// [useSingBoxProxy] sing-box true
/// [forceDirect] 使 false
static HttpClientAdapter createAdapter({
bool useSingBoxProxy = true,
bool forceDirect = false,
Duration timeout = const Duration(seconds: 10),
}) {
return IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
client.connectionTimeout = timeout;
// SSL
client.badCertificateCallback = (cert, host, port) => true;
if (forceDirect) {
client.findProxy = (url) {
KRLogUtil.kr_i('🔍 请求使用直连: $url', tag: 'KRHttpAdapterUtil');
return 'DIRECT';
};
} else if (useSingBoxProxy) {
client.findProxy = (url) {
try {
final proxyConfig = KRSingBoxImp.instance.kr_buildProxyRule();
KRLogUtil.kr_i('🔍 请求使用代理: $proxyConfig, url: $url', tag: 'KRHttpAdapterUtil');
return proxyConfig;
} catch (e) {
KRLogUtil.kr_w('⚠️ 获取代理配置异常,回退到 DIRECT: $e', tag: 'KRHttpAdapterUtil');
return 'DIRECT';
}
};
}
return client;
},
);
}
}

View File

@ -25,8 +25,9 @@ class KRWindowManager with WindowListener, TrayListener {
KRLogUtil.kr_i('kr_initWindowManager: 窗口管理器已初始化'); KRLogUtil.kr_i('kr_initWindowManager: 窗口管理器已初始化');
const WindowOptions windowOptions = WindowOptions( const WindowOptions windowOptions = WindowOptions(
size: Size(800, 668), size: Size(420, 800),
minimumSize: Size(400, 334), minimumSize: Size(420, 800),
maximumSize: Size(420, 800),
center: true, center: true,
backgroundColor: Colors.white, backgroundColor: Colors.white,
skipTaskbar: false, skipTaskbar: false,
@ -46,16 +47,20 @@ class KRWindowManager with WindowListener, TrayListener {
if (Platform.isWindows) { if (Platform.isWindows) {
await windowManager.setTitleBarStyle(TitleBarStyle.normal); await windowManager.setTitleBarStyle(TitleBarStyle.normal);
await windowManager.setTitle('HiFastVPN'); await windowManager.setTitle('HiFastVPN');
await windowManager.setSize(const Size(800, 668)); await windowManager.setSize(const Size(420, 800));
await windowManager.setMinimumSize(const Size(400, 334)); await windowManager.setMinimumSize(const Size(420, 800));
await windowManager.setMaximumSize(const Size(420, 800));
await windowManager.setResizable(false);
await windowManager.center(); await windowManager.center();
await windowManager.show(); await windowManager.show();
// //
await windowManager.setPreventClose(true); await windowManager.setPreventClose(true);
} else { } else {
await windowManager.setTitle('HiFastVPN'); await windowManager.setTitle('HiFastVPN');
await windowManager.setSize(const Size(800, 668)); await windowManager.setSize(const Size(420, 800));
await windowManager.setMinimumSize(const Size(400, 334)); await windowManager.setMinimumSize(const Size(420, 800));
await windowManager.setMaximumSize(const Size(420, 800));
await windowManager.setResizable(false);
await windowManager.center(); await windowManager.center();
} }

View File

@ -1,5 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart'; import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import 'package:kaer_with_panels/app/utils/kr_windows_process_util.dart';
/// Windows DNS /// Windows DNS
/// ///
@ -38,28 +39,48 @@ class KRWindowsDnsUtil {
} }
try { try {
KRLogUtil.kr_i('📦 开始备份 Windows DNS 设置...', tag: 'WindowsDNS'); // 🔒 5
return await Future.value(() async {
KRLogUtil.kr_i('📦 开始备份 Windows DNS 设置...', tag: 'WindowsDNS');
// 1. // 1.
final interfaceName = await _kr_getPrimaryNetworkInterface(); final interfaceName = await _kr_getPrimaryNetworkInterface();
if (interfaceName == null) { if (interfaceName == null) {
KRLogUtil.kr_e('❌ 无法获取主网络接口', tag: 'WindowsDNS'); KRLogUtil.kr_e('❌ 无法获取主网络接口', tag: 'WindowsDNS');
return false; return false;
} }
_primaryInterfaceName = interfaceName; _primaryInterfaceName = interfaceName;
KRLogUtil.kr_i('🔍 主网络接口: $_primaryInterfaceName', tag: 'WindowsDNS'); KRLogUtil.kr_i('🔍 主网络接口: $_primaryInterfaceName', tag: 'WindowsDNS');
// 2. DNS // 2. DNS
final dnsServers = await _kr_getCurrentDnsServers(interfaceName); final dnsServers = await _kr_getCurrentDnsServers(interfaceName);
if (dnsServers.isEmpty) {
KRLogUtil.kr_w('⚠️ 当前 DNS 为空,可能是自动获取', tag: 'WindowsDNS');
_originalDnsServers = []; // DHCP
} else {
_originalDnsServers = dnsServers;
KRLogUtil.kr_i('✅ 已备份 DNS: ${dnsServers.join(", ")}', tag: 'WindowsDNS');
}
return true; // 🔧 P0修复1: 127.0.0.1 (sing-box DNS)
// 127.0.0.1 VPN 127.0.0.1 sing-box DNS
final validDnsServers = dnsServers.where((dns) => !dns.startsWith('127.')).toList();
if (validDnsServers.isEmpty) {
KRLogUtil.kr_w('⚠️ 当前 DNS 为空或全是本地地址,设为 DHCP 自动获取', tag: 'WindowsDNS');
if (dnsServers.isNotEmpty) {
KRLogUtil.kr_i(' (已过滤的本地DNS: ${dnsServers.join(", ")})', tag: 'WindowsDNS');
}
_originalDnsServers = []; // DHCP
} else {
_originalDnsServers = validDnsServers;
KRLogUtil.kr_i('✅ 已备份有效 DNS: ${validDnsServers.join(", ")}', tag: 'WindowsDNS');
if (dnsServers.length != validDnsServers.length) {
KRLogUtil.kr_i(' (已过滤掉 ${dnsServers.length - validDnsServers.length} 个本地地址)', tag: 'WindowsDNS');
}
}
return true;
}()).timeout(
const Duration(seconds: 5),
onTimeout: () {
KRLogUtil.kr_w('⏱️ DNS 备份操作超时5秒跳过备份', tag: 'WindowsDNS');
return false;
},
);
} catch (e) { } catch (e) {
KRLogUtil.kr_e('❌ 备份 DNS 设置失败: $e', tag: 'WindowsDNS'); KRLogUtil.kr_e('❌ 备份 DNS 设置失败: $e', tag: 'WindowsDNS');
return false; return false;
@ -80,22 +101,33 @@ class KRWindowsDnsUtil {
try { try {
KRLogUtil.kr_i('🔄 开始恢复 Windows DNS 设置...', tag: 'WindowsDNS'); KRLogUtil.kr_i('🔄 开始恢复 Windows DNS 设置...', tag: 'WindowsDNS');
// 1. // 🔧 P1修复:
if (_primaryInterfaceName == null) { final currentInterface = await _kr_getPrimaryNetworkInterface();
KRLogUtil.kr_w('⚠️ 没有备份的网络接口,尝试自动检测', tag: 'WindowsDNS'); if (currentInterface == null) {
_primaryInterfaceName = await _kr_getPrimaryNetworkInterface(); KRLogUtil.kr_e('❌ 无法检测当前网络接口,执行兜底恢复', tag: 'WindowsDNS');
if (_primaryInterfaceName == null) { return await _kr_fallbackRestoreDns();
KRLogUtil.kr_e('❌ 无法检测网络接口,执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
} }
// 2. DNS //
if (_primaryInterfaceName != null && _primaryInterfaceName != currentInterface) {
KRLogUtil.kr_w('⚠️ 网络接口已变化: $_primaryInterfaceName$currentInterface', tag: 'WindowsDNS');
KRLogUtil.kr_w(' 执行兜底恢复以确保当前接口DNS正常', tag: 'WindowsDNS');
_primaryInterfaceName = currentInterface; //
return await _kr_fallbackRestoreDns();
}
// 使
_primaryInterfaceName = currentInterface;
KRLogUtil.kr_i('🔍 当前网络接口: $_primaryInterfaceName', tag: 'WindowsDNS');
// 1. DNS
if (_originalDnsServers == null) { if (_originalDnsServers == null) {
KRLogUtil.kr_w('⚠️ 没有备份的 DNS执行兜底恢复', tag: 'WindowsDNS'); KRLogUtil.kr_w('⚠️ 没有备份的 DNS执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns(); return await _kr_fallbackRestoreDns();
} }
// 2. DNS
if (_originalDnsServers!.isEmpty) { if (_originalDnsServers!.isEmpty) {
// DHCP // DHCP
KRLogUtil.kr_i('🔄 恢复为 DHCP 自动获取 DNS', tag: 'WindowsDNS'); KRLogUtil.kr_i('🔄 恢复为 DHCP 自动获取 DNS', tag: 'WindowsDNS');
@ -130,6 +162,15 @@ class KRWindowsDnsUtil {
return await _kr_fallbackRestoreDns(); return await _kr_fallbackRestoreDns();
} }
// 🔧 P2优化: DNS
KRLogUtil.kr_i('🧪 测试 DNS 解析功能...', tag: 'WindowsDNS');
final canResolve = await _kr_testDnsResolution();
if (!canResolve) {
KRLogUtil.kr_w('⚠️ DNS 解析测试失败,执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
KRLogUtil.kr_i('✅ DNS 解析测试通过', tag: 'WindowsDNS');
return true; return true;
} catch (e) { } catch (e) {
KRLogUtil.kr_e('❌ 恢复 DNS 设置失败: $e', tag: 'WindowsDNS'); KRLogUtil.kr_e('❌ 恢复 DNS 设置失败: $e', tag: 'WindowsDNS');
@ -187,7 +228,7 @@ class KRWindowsDnsUtil {
Future<String?> _kr_getPrimaryNetworkInterface() async { Future<String?> _kr_getPrimaryNetworkInterface() async {
try { try {
// 使 netsh // 使 netsh
final result = await Process.run('netsh', ['interface', 'show', 'interface']); final result = await KRWindowsProcessUtil.runHidden('netsh', ['interface', 'show', 'interface']);
if (result.exitCode != 0) { if (result.exitCode != 0) {
KRLogUtil.kr_e('❌ 获取网络接口失败: ${result.stderr}', tag: 'WindowsDNS'); KRLogUtil.kr_e('❌ 获取网络接口失败: ${result.stderr}', tag: 'WindowsDNS');
@ -271,7 +312,7 @@ class KRWindowsDnsUtil {
/// DNS /// DNS
Future<List<String>> _kr_getCurrentDnsServers(String interfaceName) async { Future<List<String>> _kr_getCurrentDnsServers(String interfaceName) async {
try { try {
final result = await Process.run('netsh', [ final result = await KRWindowsProcessUtil.runHidden('netsh', [
'interface', 'interface',
'ipv4', 'ipv4',
'show', 'show',
@ -294,10 +335,9 @@ class KRWindowsDnsUtil {
final ipMatch = RegExp(r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b').firstMatch(line); final ipMatch = RegExp(r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b').firstMatch(line);
if (ipMatch != null) { if (ipMatch != null) {
final ip = ipMatch.group(0)!; final ip = ipMatch.group(0)!;
// // 🔧 127.0.0.1便DNS是否还在使用sing-box的本地DNS
if (!ip.startsWith('127.')) { // DNS时126127.0.0.1
dnsServers.add(ip); dnsServers.add(ip);
}
} }
} }
@ -325,7 +365,7 @@ class KRWindowsDnsUtil {
// 1. DNS // 1. DNS
KRLogUtil.kr_i('🔧 设置主 DNS: ${dnsServers[0]}', tag: 'WindowsDNS'); KRLogUtil.kr_i('🔧 设置主 DNS: ${dnsServers[0]}', tag: 'WindowsDNS');
var result = await Process.run('netsh', [ var result = await KRWindowsProcessUtil.runHidden('netsh', [
'interface', 'interface',
'ipv4', 'ipv4',
'set', 'set',
@ -345,7 +385,7 @@ class KRWindowsDnsUtil {
if (dnsServers.length > 1) { if (dnsServers.length > 1) {
for (int i = 1; i < dnsServers.length; i++) { for (int i = 1; i < dnsServers.length; i++) {
KRLogUtil.kr_i('🔧 设置备用 DNS ${i}: ${dnsServers[i]}', tag: 'WindowsDNS'); KRLogUtil.kr_i('🔧 设置备用 DNS ${i}: ${dnsServers[i]}', tag: 'WindowsDNS');
result = await Process.run('netsh', [ result = await KRWindowsProcessUtil.runHidden('netsh', [
'interface', 'interface',
'ipv4', 'ipv4',
'add', 'add',
@ -384,7 +424,7 @@ class KRWindowsDnsUtil {
try { try {
KRLogUtil.kr_i('🔧 设置 DNS 为自动获取 (DHCP)', tag: 'WindowsDNS'); KRLogUtil.kr_i('🔧 设置 DNS 为自动获取 (DHCP)', tag: 'WindowsDNS');
final result = await Process.run('netsh', [ final result = await KRWindowsProcessUtil.runHidden('netsh', [
'interface', 'interface',
'ipv4', 'ipv4',
'set', 'set',
@ -416,7 +456,7 @@ class KRWindowsDnsUtil {
try { try {
KRLogUtil.kr_i('🔄 刷新 DNS 缓存...', tag: 'WindowsDNS'); KRLogUtil.kr_i('🔄 刷新 DNS 缓存...', tag: 'WindowsDNS');
final result = await Process.run('ipconfig', ['/flushdns']); final result = await KRWindowsProcessUtil.runHidden('ipconfig', ['/flushdns']);
if (result.exitCode == 0) { if (result.exitCode == 0) {
KRLogUtil.kr_i('✅ DNS 缓存已刷新', tag: 'WindowsDNS'); KRLogUtil.kr_i('✅ DNS 缓存已刷新', tag: 'WindowsDNS');
@ -428,6 +468,51 @@ class KRWindowsDnsUtil {
} }
} }
/// 🔧 P2优化: DNS
///
/// nslookup
/// true DNS false DNS
Future<bool> _kr_testDnsResolution() async {
try {
//
final testDomains = ['www.baidu.com', 'www.qq.com', 'dns.alidns.com'];
for (var domain in testDomains) {
try {
// 使 nslookup DNS 2
final result = await KRWindowsProcessUtil.runHidden(
'nslookup',
[domain],
).timeout(
const Duration(seconds: 2),
onTimeout: () {
return ProcessResult(0, 1, '', 'Timeout');
},
);
if (result.exitCode == 0) {
final output = result.stdout.toString();
// IP
if (output.contains('Address:') || output.contains('地址:')) {
KRLogUtil.kr_i('✅ DNS 解析测试通过: $domain', tag: 'WindowsDNS');
return true;
}
}
} catch (e) {
//
continue;
}
}
//
KRLogUtil.kr_w('⚠️ 所有测试域名解析均失败', tag: 'WindowsDNS');
return false;
} catch (e) {
KRLogUtil.kr_e('❌ DNS 解析测试异常: $e', tag: 'WindowsDNS');
return false;
}
}
/// ///
/// ///
/// 退 /// 退

View File

@ -0,0 +1,594 @@
import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
class KRWindowsProcessUtil {
/// 🔍 true
static const bool _debugCommandExecution = true; //
static Future<ProcessResult> runHidden(String executable, List<String> arguments) async {
if (!Platform.isWindows) {
return Process.run(executable, arguments);
}
final result = await _runHiddenWindows(executable, arguments);
return result;
}
static Future<int> startHidden(String executable, List<String> arguments) async {
if (!Platform.isWindows) {
final process = await Process.start(executable, arguments);
return process.pid;
}
return _startHiddenWindows(executable, arguments);
}
static String _buildCommandLine(String executable, List<String> arguments) {
final parts = <String>[_quoteArgument(executable)];
for (final arg in arguments) {
parts.add(_quoteArgument(arg));
}
return parts.join(' ');
}
static bool _shouldSearchPath(String executable) {
if (executable.isEmpty) {
return true;
}
return !(executable.contains('\\') || executable.contains('/') || executable.contains(':'));
}
static String _quoteArgument(String value) {
if (value.isEmpty) {
return '""';
}
final needsQuotes = value.contains(' ') || value.contains('\t') || value.contains('"');
if (!needsQuotes) {
return value;
}
final buffer = StringBuffer('"');
var backslashes = 0;
for (var i = 0; i < value.length; i++) {
final char = value[i];
if (char == '\\') {
backslashes++;
continue;
}
if (char == '"') {
buffer.write('\\' * (backslashes * 2 + 1));
buffer.write('"');
backslashes = 0;
continue;
}
if (backslashes > 0) {
buffer.write('\\' * backslashes);
backslashes = 0;
}
buffer.write(char);
}
if (backslashes > 0) {
buffer.write('\\' * (backslashes * 2));
}
buffer.write('"');
return buffer.toString();
}
static Future<ProcessResult> _runHiddenWindows(String executable, List<String> arguments) async {
final stdoutPipe = _createPipe();
final stderrPipe = _createPipe();
final startupInfo = calloc<STARTUPINFO>();
final processInfo = calloc<PROCESS_INFORMATION>();
final commandLine = _buildCommandLine(executable, arguments).toNativeUtf16();
final applicationName = _shouldSearchPath(executable) ? nullptr : executable.toNativeUtf16();
final stdInput = _getStdInputHandle();
startupInfo.ref
..cb = sizeOf<STARTUPINFO>()
..dwFlags = STARTF_USESTDHANDLES
..hStdInput = stdInput
..hStdOutput = stdoutPipe.write
..hStdError = stderrPipe.write;
final created = _CreateProcessW(
applicationName,
commandLine,
nullptr,
nullptr,
TRUE,
CREATE_NO_WINDOW,
nullptr,
nullptr,
startupInfo,
processInfo,
);
calloc.free(commandLine);
if (applicationName != nullptr) {
calloc.free(applicationName);
}
if (created == 0) {
_closeHandle(stdoutPipe.read);
_closeHandle(stdoutPipe.write);
_closeHandle(stderrPipe.read);
_closeHandle(stderrPipe.write);
calloc.free(startupInfo);
calloc.free(processInfo);
throw Exception('CreateProcessW failed: ${_GetLastError()}');
}
_closeHandle(stdoutPipe.write);
_closeHandle(stderrPipe.write);
final output = await _collectOutput(processInfo.ref.hProcess, stdoutPipe.read, stderrPipe.read);
final exitCode = _getExitCode(processInfo.ref.hProcess);
_closeHandle(stdoutPipe.read);
_closeHandle(stderrPipe.read);
_closeHandle(processInfo.ref.hThread);
_closeHandle(processInfo.ref.hProcess);
final pid = processInfo.ref.dwProcessId;
calloc.free(startupInfo);
calloc.free(processInfo);
return ProcessResult(pid, exitCode, output.stdout, output.stderr);
}
static Future<int> _startHiddenWindows(String executable, List<String> arguments) async {
final startupInfo = calloc<STARTUPINFO>();
final processInfo = calloc<PROCESS_INFORMATION>();
final commandLine = _buildCommandLine(executable, arguments).toNativeUtf16();
final applicationName = _shouldSearchPath(executable) ? nullptr : executable.toNativeUtf16();
startupInfo.ref.cb = sizeOf<STARTUPINFO>();
final created = _CreateProcessW(
applicationName,
commandLine,
nullptr,
nullptr,
FALSE,
CREATE_NO_WINDOW,
nullptr,
nullptr,
startupInfo,
processInfo,
);
calloc.free(commandLine);
if (applicationName != nullptr) {
calloc.free(applicationName);
}
if (created == 0) {
calloc.free(startupInfo);
calloc.free(processInfo);
throw Exception('CreateProcessW failed: ${_GetLastError()}');
}
final pid = processInfo.ref.dwProcessId;
_closeHandle(processInfo.ref.hThread);
_closeHandle(processInfo.ref.hProcess);
calloc.free(startupInfo);
calloc.free(processInfo);
return pid;
}
static _Pipe _createPipe() {
final readHandle = calloc<Pointer<Void>>();
final writeHandle = calloc<Pointer<Void>>();
final securityAttributes = calloc<SECURITY_ATTRIBUTES>();
securityAttributes.ref
..nLength = sizeOf<SECURITY_ATTRIBUTES>()
..bInheritHandle = TRUE
..lpSecurityDescriptor = nullptr;
final created = _CreatePipe(readHandle, writeHandle, securityAttributes, 0);
calloc.free(securityAttributes);
if (created == 0) {
calloc.free(readHandle);
calloc.free(writeHandle);
throw Exception('CreatePipe failed: ${_GetLastError()}');
}
final readValue = readHandle.value;
final writeValue = writeHandle.value;
calloc.free(readHandle);
calloc.free(writeHandle);
final infoResult = _SetHandleInformation(readValue, HANDLE_FLAG_INHERIT, 0);
if (infoResult == 0) {
_closeHandle(readValue);
_closeHandle(writeValue);
throw Exception('SetHandleInformation failed: ${_GetLastError()}');
}
return _Pipe(readValue, writeValue);
}
static Pointer<Void> _getStdInputHandle() {
final handle = _GetStdHandle(STD_INPUT_HANDLE);
if (handle == INVALID_HANDLE_VALUE || handle == 0) {
return nullptr;
}
return Pointer<Void>.fromAddress(handle);
}
static Future<_ProcessOutput> _collectOutput(
Pointer<Void> process,
Pointer<Void> stdoutHandle,
Pointer<Void> stderrHandle,
) async {
final stdoutBuilder = BytesBuilder();
final stderrBuilder = BytesBuilder();
while (true) {
final stdoutRead = _drainPipe(stdoutHandle, stdoutBuilder);
final stderrRead = _drainPipe(stderrHandle, stderrBuilder);
final waitResult = _WaitForSingleObject(process, 0);
if (waitResult == WAIT_OBJECT_0) {
break;
}
if (waitResult == WAIT_FAILED) {
throw Exception('WaitForSingleObject failed: ${_GetLastError()}');
}
if (!stdoutRead && !stderrRead) {
await Future.delayed(const Duration(milliseconds: 10));
} else {
await Future<void>.delayed(Duration.zero);
}
}
while (_drainPipe(stdoutHandle, stdoutBuilder) || _drainPipe(stderrHandle, stderrBuilder)) {
await Future<void>.delayed(Duration.zero);
}
return _ProcessOutput(
_decodeOutput(stdoutBuilder),
_decodeOutput(stderrBuilder),
);
}
static bool _drainPipe(Pointer<Void> handle, BytesBuilder builder) {
final buffer = calloc<Uint8>(4096);
final bytesRead = calloc<Uint32>();
final available = calloc<Uint32>();
var didRead = false;
while (true) {
final peekOk = _PeekNamedPipe(handle, nullptr, 0, nullptr, available, nullptr);
if (peekOk == 0 || available.value == 0) {
break;
}
final toRead = available.value < 4096 ? available.value : 4096;
final ok = _ReadFile(handle, buffer.cast<Void>(), toRead, bytesRead, nullptr);
final read = ok == 0 ? 0 : bytesRead.value;
if (read == 0) {
break;
}
builder.add(buffer.asTypedList(read));
didRead = true;
}
calloc.free(buffer);
calloc.free(bytesRead);
calloc.free(available);
return didRead;
}
static String _decodeOutput(BytesBuilder builder) {
if (builder.length == 0) {
return '';
}
final bytes = builder.toBytes();
try {
return systemEncoding.decode(bytes);
} catch (_) {
return utf8.decode(bytes, allowMalformed: true);
}
}
static int _getExitCode(Pointer<Void> process) {
final exitCode = calloc<Uint32>();
final ok = _GetExitCodeProcess(process, exitCode);
final code = ok == 0 ? -1 : exitCode.value;
calloc.free(exitCode);
return code;
}
static void _closeHandle(Pointer<Void> handle) {
if (handle == nullptr) {
return;
}
_CloseHandle(handle);
}
// 🔧 WinINet API helpers for proxy settings
///
static String? queryWindowsProxyServer() {
if (!Platform.isWindows) return null;
try {
final bufferSize = calloc<Uint32>();
bufferSize.value = sizeOf<INTERNET_PROXY_INFO>();
final proxyInfo = calloc<INTERNET_PROXY_INFO>();
final result = _InternetQueryOptionW(nullptr, INTERNET_OPTION_PROXY, proxyInfo.cast<Void>(), bufferSize);
if (result == 0) {
calloc.free(bufferSize);
calloc.free(proxyInfo);
return null;
}
final proxyServer = proxyInfo.ref.lpszProxy.toDartString();
calloc.free(bufferSize);
calloc.free(proxyInfo);
return proxyServer.isEmpty ? null : proxyServer;
} catch (e) {
return null;
}
}
///
static bool setWindowsProxyServer(String? server) {
if (!Platform.isWindows) return false;
try {
final proxyInfo = calloc<INTERNET_PROXY_INFO>();
if (server != null && server.isNotEmpty) {
//
proxyInfo.ref.dwAccessType = INTERNET_OPEN_TYPE_PROXY;
proxyInfo.ref.lpszProxy = server.toNativeUtf16();
proxyInfo.ref.lpszProxyBypass = ''.toNativeUtf16();
} else {
//
proxyInfo.ref.dwAccessType = INTERNET_OPEN_TYPE_DIRECT;
proxyInfo.ref.lpszProxy = nullptr;
proxyInfo.ref.lpszProxyBypass = nullptr;
}
final result = _InternetSetOptionW(
nullptr,
INTERNET_OPTION_PROXY,
proxyInfo.cast<Void>(),
sizeOf<INTERNET_PROXY_INFO>(),
);
if (server != null && server.isNotEmpty) {
calloc.free(proxyInfo.ref.lpszProxy);
calloc.free(proxyInfo.ref.lpszProxyBypass);
}
calloc.free(proxyInfo);
return result != 0;
} catch (e) {
return false;
}
}
///
static bool disableWindowsProxy() {
return setWindowsProxyServer(null);
}
}
class _Pipe {
final Pointer<Void> read;
final Pointer<Void> write;
_Pipe(this.read, this.write);
}
class _ProcessOutput {
final String stdout;
final String stderr;
_ProcessOutput(this.stdout, this.stderr);
}
const int TRUE = 1;
const int FALSE = 0;
const int STARTF_USESTDHANDLES = 0x00000100;
const int CREATE_NO_WINDOW = 0x08000000;
const int HANDLE_FLAG_INHERIT = 0x00000001;
const int WAIT_OBJECT_0 = 0x00000000;
const int WAIT_FAILED = 0xFFFFFFFF;
const int STD_INPUT_HANDLE = -10;
const int INVALID_HANDLE_VALUE = -1;
final class SECURITY_ATTRIBUTES extends Struct {
@Uint32()
external int nLength;
external Pointer<Void> lpSecurityDescriptor;
@Int32()
external int bInheritHandle;
}
final class STARTUPINFO extends Struct {
@Uint32()
external int cb;
external Pointer<Utf16> lpReserved;
external Pointer<Utf16> lpDesktop;
external Pointer<Utf16> lpTitle;
@Uint32()
external int dwX;
@Uint32()
external int dwY;
@Uint32()
external int dwXSize;
@Uint32()
external int dwYSize;
@Uint32()
external int dwXCountChars;
@Uint32()
external int dwYCountChars;
@Uint32()
external int dwFillAttribute;
@Uint32()
external int dwFlags;
@Uint16()
external int wShowWindow;
@Uint16()
external int cbReserved2;
external Pointer<Uint8> lpReserved2;
external Pointer<Void> hStdInput;
external Pointer<Void> hStdOutput;
external Pointer<Void> hStdError;
}
final class PROCESS_INFORMATION extends Struct {
external Pointer<Void> hProcess;
external Pointer<Void> hThread;
@Uint32()
external int dwProcessId;
@Uint32()
external int dwThreadId;
}
final DynamicLibrary _kernel32 = DynamicLibrary.open('kernel32.dll');
final _CreatePipe = _kernel32.lookupFunction<
Int32 Function(Pointer<Pointer<Void>>, Pointer<Pointer<Void>>, Pointer<SECURITY_ATTRIBUTES>, Uint32),
int Function(Pointer<Pointer<Void>>, Pointer<Pointer<Void>>, Pointer<SECURITY_ATTRIBUTES>, int)>(
'CreatePipe',
);
final _SetHandleInformation = _kernel32.lookupFunction<
Int32 Function(Pointer<Void>, Uint32, Uint32),
int Function(Pointer<Void>, int, int)>(
'SetHandleInformation',
);
final _CreateProcessW = _kernel32.lookupFunction<
Int32 Function(
Pointer<Utf16>,
Pointer<Utf16>,
Pointer<SECURITY_ATTRIBUTES>,
Pointer<SECURITY_ATTRIBUTES>,
Int32,
Uint32,
Pointer<Void>,
Pointer<Utf16>,
Pointer<STARTUPINFO>,
Pointer<PROCESS_INFORMATION>,
),
int Function(
Pointer<Utf16>,
Pointer<Utf16>,
Pointer<SECURITY_ATTRIBUTES>,
Pointer<SECURITY_ATTRIBUTES>,
int,
int,
Pointer<Void>,
Pointer<Utf16>,
Pointer<STARTUPINFO>,
Pointer<PROCESS_INFORMATION>,
)>(
'CreateProcessW',
);
final _PeekNamedPipe = _kernel32.lookupFunction<
Int32 Function(Pointer<Void>, Pointer<Void>, Uint32, Pointer<Uint32>, Pointer<Uint32>, Pointer<Uint32>),
int Function(Pointer<Void>, Pointer<Void>, int, Pointer<Uint32>, Pointer<Uint32>, Pointer<Uint32>)>(
'PeekNamedPipe',
);
final _ReadFile = _kernel32.lookupFunction<
Int32 Function(Pointer<Void>, Pointer<Void>, Uint32, Pointer<Uint32>, Pointer<Void>),
int Function(Pointer<Void>, Pointer<Void>, int, Pointer<Uint32>, Pointer<Void>)>(
'ReadFile',
);
final _CloseHandle = _kernel32.lookupFunction<
Int32 Function(Pointer<Void>),
int Function(Pointer<Void>)>(
'CloseHandle',
);
final _WaitForSingleObject = _kernel32.lookupFunction<
Uint32 Function(Pointer<Void>, Uint32),
int Function(Pointer<Void>, int)>(
'WaitForSingleObject',
);
final _GetStdHandle = _kernel32.lookupFunction<
IntPtr Function(Int32),
int Function(int)>(
'GetStdHandle',
);
final _GetExitCodeProcess = _kernel32.lookupFunction<
Int32 Function(Pointer<Void>, Pointer<Uint32>),
int Function(Pointer<Void>, Pointer<Uint32>)>(
'GetExitCodeProcess',
);
final _GetLastError = _kernel32.lookupFunction<
Uint32 Function(),
int Function()>(
'GetLastError',
);
// 🔧 WinINet API for proxy settings - reg
final DynamicLibrary _wininet = DynamicLibrary.open('wininet.dll');
const int INTERNET_OPTION_PROXY = 38;
const int INTERNET_OPEN_TYPE_PROXY = 3;
const int INTERNET_OPEN_TYPE_DIRECT = 1;
final class INTERNET_PROXY_INFO extends Struct {
@Int32()
external int dwAccessType;
external Pointer<Utf16> lpszProxy;
external Pointer<Utf16> lpszProxyBypass;
}
/// WinINet InternetSetOption API -
final _InternetSetOptionW = _wininet.lookupFunction<
Int32 Function(Pointer<Void>, Uint32, Pointer<Void>, Uint32),
int Function(Pointer<Void>, int, Pointer<Void>, int)>(
'InternetSetOptionW',
);
/// WinINet InternetQueryOption API -
final _InternetQueryOptionW = _wininet.lookupFunction<
Int32 Function(Pointer<Void>, Uint32, Pointer<Void>, Pointer<Uint32>),
int Function(Pointer<Void>, int, Pointer<Void>, Pointer<Uint32>)>(
'InternetQueryOptionW',
);

View File

@ -106,7 +106,7 @@ class _HIDialogState extends State<HIDialog> {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(23.r), borderRadius: BorderRadius.circular(23.r),
), ),
minimumSize: Size.fromHeight(40.w), minimumSize: Size(85.w, 40.w),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap, tapTargetSize: MaterialTapTargetSize.shrinkWrap,
), ),
@ -120,7 +120,7 @@ class _HIDialogState extends State<HIDialog> {
), ),
) )
: Text( : Text(
widget.confirmText ?? AppTranslations.kr_dialog.kr_confirm, widget.confirmText ?? (widget.cancelText == null ? '好的' : AppTranslations.kr_dialog.kr_confirm),
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -136,96 +136,106 @@ class _HIDialogState extends State<HIDialog> {
final dialog = Dialog( final dialog = Dialog(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
insetPadding: EdgeInsets.all(20.w), insetPadding: EdgeInsets.all(20.w),
child: ClipRRect( child: Transform.translate(
borderRadius: BorderRadius.circular(34.r), offset: Offset(0, -30.w),
child: BackdropFilter( child: ClipRRect(
filter: ImageFilter.blur(sigmaX: 26, sigmaY: 26), // borderRadius: BorderRadius.circular(34.r),
child: Container( child: BackdropFilter(
width: 245.w, filter: ImageFilter.blur(sigmaX: 26, sigmaY: 26), //
padding: EdgeInsets.fromLTRB(30.w, 16.w, 30.w, 16.w), child: Container(
decoration: BoxDecoration( width: 245.w,
color: const Color(0xF5F5F5).withOpacity(0.6), // padding: EdgeInsets.fromLTRB(30.w, 16.w, 30.w, 16.w),
borderRadius: BorderRadius.circular(34.r), decoration: BoxDecoration(
border: Border.all( color: const Color(0xFFDEDEDE), //
color: Colors.white.withOpacity(0.2), // borderRadius: BorderRadius.circular(34.r),
width: 1.5, border: Border.all(
color: Colors.white.withOpacity(0.2), //
width: 1.5,
),
), ),
), child: Column(
child: Column( mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, children: [
children: [ if (widget.title != null) ...[
if (widget.title != null) ...[ Align(
Align( alignment: Alignment.centerLeft,
alignment: Alignment.centerLeft, child: Text(
child: Text( widget.title!,
widget.title!, style: TextStyle(
style: TextStyle( fontSize: 14.sp,
fontSize: 14.sp, fontWeight: FontWeight.w600,
fontWeight: FontWeight.w600, color: Colors.black,
color: Colors.black, height: 1.3,
fontFamily: 'AlibabaPuHuiTi-Medium', ),
height: 1.3,
), ),
), ),
), SizedBox(height: 4.w),
SizedBox(height: 4.w), ],
], if (widget.message != null || widget.customMessageWidget != null) ...[
if (widget.message != null || widget.customMessageWidget != null) ...[ Container(
Container( constraints: BoxConstraints(maxHeight: 200.h),
constraints: BoxConstraints(maxHeight: 200.h), child: SingleChildScrollView(
child: SingleChildScrollView( child: widget.customMessageWidget ??
child: widget.customMessageWidget ?? Text(
Text( widget.message!,
widget.message!, textAlign: TextAlign.left,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 14.sp,
color: Colors.black,
height: 1.4,
fontFamily: 'AlibabaPuHuiTi-Medium',
),
),
),
),
],
if (widget.confirmText != null || widget.cancelText != null) ...[
SizedBox(height: 28.w),
Row(
children: [
if (widget.confirmText != null) Expanded(child: _buildConfirmButton()),
if (widget.cancelText != null && widget.confirmText != null)
SizedBox(width: 12.w),
if (widget.cancelText != null)
Expanded(
child: TextButton(
onPressed: () {
Get.back();
widget.onCancel?.call();
},
style: TextButton.styleFrom(
backgroundColor: const Color(0xFFFF00B7),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(23.r),
),
minimumSize: Size.fromHeight(40.w),
padding: EdgeInsets.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: Text(
widget.cancelText!,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: Colors.black, color: Colors.black,
height: 1.4,
fontFamily: 'AlibabaPuHuiTi-Medium', fontFamily: 'AlibabaPuHuiTi-Medium',
), ),
), ),
), ),
),
],
if (widget.confirmText != null || widget.cancelText != null || (widget.confirmText == null && widget.cancelText == null)) ...[
SizedBox(height: 12.w),
if (widget.confirmText == null && widget.cancelText == null)
Center(
child: SizedBox(
width: 85.w,
child: _buildConfirmButton(),
), ),
], )
), else
] Row(
], children: [
if (widget.confirmText != null) Expanded(child: _buildConfirmButton()),
if (widget.cancelText != null && widget.confirmText != null)
SizedBox(width: 12.w),
if (widget.cancelText != null)
Expanded(
child: TextButton(
onPressed: () {
Get.back();
widget.onCancel?.call();
},
style: TextButton.styleFrom(
backgroundColor: const Color(0xFFFF00B7),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(23.r),
),
minimumSize: Size(85.w, 40.w),
padding: EdgeInsets.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: Text(
widget.cancelText!,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: Colors.black,
fontFamily: 'AlibabaPuHuiTi-Medium',
),
),
),
),
],
),
]
],
),
), ),
), ),
), ),

View File

@ -6,7 +6,8 @@ import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
import 'dart:math' as math; // math 使 pi import 'dart:math' as math; // math 使 pi
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../routes/app_pages.dart'; import 'package:kaer_with_panels/app/routes/app_pages.dart';
import 'package:url_launcher/url_launcher.dart';
/// ///
class HICollapsibleItem { class HICollapsibleItem {
@ -61,15 +62,11 @@ class _HICollapsibleItemWidgetState extends State<HICollapsibleItemWidget> {
color: const Color(0xFFADFF5B), // color: const Color(0xFFADFF5B), //
), ),
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () { ..onTap = () async {
// final Uri url = Uri.parse('https://hifastvpn.com/help');
// url_launcher if (!await launchUrl(url, mode: LaunchMode.externalApplication)) {
Get.toNamed( Get.snackbar('错误', '无法打开链接: $url');
Routes.KR_WEBVIEW, }
arguments: {
'url': 'https://www.baidu.com',
},
);
}, },
), ),
); );

View File

@ -1,117 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/localization/kr_language_utils.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
///
class KRLanguageSwitchDialog extends StatelessWidget {
const KRLanguageSwitchDialog({super.key});
///
static Future<void> kr_show() async {
final isChineseRegion = await KRLanguageUtils.checkInitialLanguage();
if (isChineseRegion) {
await Get.dialog(
const KRLanguageSwitchDialog(),
barrierDismissible: false,
);
}
}
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.transparent,
child: Container(
width: 280.w,
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 24.h),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(24.r),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
//
KrLocalImage(
imageName: 'language_switch',
width: 120.w,
height: 120.h,
),
SizedBox(height: 16.h),
//
Text(
'根据您所在地区以及您的语言设置是否切换到中文语言?',
textAlign: TextAlign.center,
style: KrAppTextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
SizedBox(height: 24.h),
//
_kr_buildButton(
context: context,
text: '切换',
isPrimary: true,
onTap: () async {
final zhLanguage = KRLanguage.values.firstWhere(
(lang) => lang.countryCode == 'zh',
orElse: () => KRLanguage.zh,
);
await KRLanguageUtils.switchLanguage(zhLanguage);
Get.back();
},
),
SizedBox(height: 12.h),
//
_kr_buildButton(
context: context,
text: '不切换',
isPrimary: false,
onTap: () => Get.back(),
),
],
),
),
);
}
///
Widget _kr_buildButton({
required BuildContext context,
required String text,
required bool isPrimary,
required VoidCallback onTap,
}) {
return InkWell(
onTap: onTap,
child: Container(
width: double.infinity,
height: 44.h,
decoration: BoxDecoration(
color: isPrimary ? Colors.blue : Colors.transparent,
borderRadius: BorderRadius.circular(22.r),
border: isPrimary
? null
: Border.all(
color: Colors.blue,
width: 1,
),
),
alignment: Alignment.center,
child: Text(
text,
style: KrAppTextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: isPrimary ? Colors.white : Colors.blue,
),
),
),
);
}
}

View File

@ -42,9 +42,8 @@ class KRSubscriptionExpiryText extends StatelessWidget {
final day = expireDateTime.day.toString().padLeft(2, '0'); final day = expireDateTime.day.toString().padLeft(2, '0');
final hour = expireDateTime.hour.toString().padLeft(2, '0'); final hour = expireDateTime.hour.toString().padLeft(2, '0');
final minute = expireDateTime.minute.toString().padLeft(2, '0'); final minute = expireDateTime.minute.toString().padLeft(2, '0');
final second = expireDateTime.second.toString().padLeft(2, '0');
final formattedDateTime = '$year/$month/$day $hour:$minute:$second'; final formattedDateTime = '$year/$month/$day $hour:$minute';
expiryText = '到期时间:$formattedDateTime'; expiryText = '到期时间:$formattedDateTime';
} }
} }

View File

@ -110,41 +110,28 @@ Widget _myApp(GetxTranslations translations, Locale initialLocale) {
initialRoute: Routes.KR_SPLASH, initialRoute: Routes.KR_SPLASH,
getPages: AppPages.routes, getPages: AppPages.routes,
builder: (context, child) { builder: (context, child) {
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { /// - 使
/// ScreenUtil.init(
ScreenUtil.init(context, context,
designSize: const Size(868, 668), minTextAdapt: true); designSize: const Size(375, 812),
} else { minTextAdapt: true,
/// );
ScreenUtil.init(context,
designSize: const Size(375, 667), minTextAdapt: true);
}
// child = FlutterEasyLoading(child: child); // // child = FlutterEasyLoading(child: child); //
// //
Widget wrappedChild = Listener( Widget wrappedChild = Listener(
onPointerDown: (_) async { onPointerDown: (_) async {
//
// try {
// final store = FMTCStore(KRFMTC.kr_storeName);
// if (!await store.manage.ready) {
// await store.manage.create();
// await KRFMTC.kr_initMapCache();
// }
// } catch (e) {
// print('地图缓存初始化失败: $e');
// }
}, },
child: child, child: child,
); );
// Mac //
if (Platform.isMacOS) { if (Platform.isMacOS || Platform.isWindows) {
wrappedChild = MediaQuery( wrappedChild = MediaQuery(
data: MediaQuery.of(context).copyWith( data: MediaQuery.of(context).copyWith(
padding: MediaQuery.of(context).padding.copyWith( padding: MediaQuery.of(context).padding.copyWith(
top: 10.w, // Mac top: 24.w, //
), ),
), ),
child: wrappedChild, child: wrappedChild,
@ -181,9 +168,15 @@ Widget _myApp(GetxTranslations translations, Locale initialLocale) {
transitionDuration: TransitionConfig.defaultDuration, // transitionDuration: TransitionConfig.defaultDuration, //
customTransition: TransitionConfig.createDefaultTransition(), customTransition: TransitionConfig.createDefaultTransition(),
routingCallback: (routing) { routingCallback: (routing) {
if (routing == null) return; if (routing == null || routing.current == null) return;
if(Routes.KR_PURCHASE_MEMBERSHIP.contains(routing.current)) return;
// final String current = routing.current!;
print('🔥 routing.current: $current');
// UI /
GlobalOverlayService.instance.updateCurrentRoute(current);
//
const showButtonRoutes = [ const showButtonRoutes = [
Routes.MR_LOGIN, Routes.MR_LOGIN,
Routes.HI_MENU, Routes.HI_MENU,
@ -191,11 +184,22 @@ Widget _myApp(GetxTranslations translations, Locale initialLocale) {
Routes.HI_USER_INFO, Routes.HI_USER_INFO,
Routes.KR_ORDER_STATUS, Routes.KR_ORDER_STATUS,
]; ];
print('routing.current${routing.current}');
GlobalOverlayService.instance.updateSubscriptionButtonColor(null); // 使
if (showButtonRoutes.contains(routing.current)) { bool isPurchasePage = current == Routes.KR_PURCHASE_MEMBERSHIP ||
current.contains(Routes.KR_PURCHASE_MEMBERSHIP) ||
Routes.KR_PURCHASE_MEMBERSHIP.contains(current);
if (isPurchasePage) {
//
GlobalOverlayService.instance.updateSubscriptionButtonColor(Colors.transparent);
GlobalOverlayService.instance.safeShowSubscriptionButton();
} else if (showButtonRoutes.any((r) => current == r || current.contains(r) || r.contains(current))) {
//
GlobalOverlayService.instance.updateSubscriptionButtonColor(null);
GlobalOverlayService.instance.safeShowSubscriptionButton(); GlobalOverlayService.instance.safeShowSubscriptionButton();
} else { } else {
//
GlobalOverlayService.instance.hideSubscriptionButton(); GlobalOverlayService.instance.hideSubscriptionButton();
} }
}, },

View File

@ -16,7 +16,6 @@ import screen_retriever_macos
import share_plus import share_plus
import tray_manager import tray_manager
import url_launcher_macos import url_launcher_macos
import webview_flutter_wkwebview
import window_manager import window_manager
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
@ -31,6 +30,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
} }

View File

@ -5,26 +5,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: _fe_analyzer_shared name: _fe_analyzer_shared
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "67.0.0" version: "85.0.0"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
name: analyzer name: analyzer
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" sha256: f4ad0fea5f102201015c9aae9d93bc02f75dd9491529a8c21f88d17a8523d44c
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.4.1" version: "7.6.0"
analyzer_plugin: analyzer_plugin:
dependency: transitive dependency: transitive
description: description:
name: analyzer_plugin name: analyzer_plugin
sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" sha256: a5ab7590c27b779f3d4de67f31c4109dbe13dd7339f86461a6f2a8ab2594d8ce
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.11.3" version: "0.13.4"
ansicolor: ansicolor:
dependency: transitive dependency: transitive
description: description:
@ -37,10 +37,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: archive name: archive
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.6.1" version: "4.0.7"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -77,10 +77,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: build name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.1" version: "2.4.2"
build_config: build_config:
dependency: transitive dependency: transitive
description: description:
@ -101,26 +101,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: build_resolvers name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.2" version: "2.4.4"
build_runner: build_runner:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.13" version: "2.4.15"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
name: build_runner_core name: build_runner_core
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.3.2" version: "8.0.0"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@ -293,34 +293,42 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: custom_lint_core name: custom_lint_core
sha256: a85e8f78f4c52f6c63cdaf8c872eb573db0231dcdf3c3a5906d493c1f8bc20e6 sha256: "31110af3dde9d29fb10828ca33f1dce24d2798477b167675543ce3d208dee8be"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.3" version: "0.7.5"
custom_lint_visitor:
dependency: transitive
description:
name: custom_lint_visitor
sha256: "4a86a0d8415a91fbb8298d6ef03e9034dc8e323a599ddc4120a0e36c433983a2"
url: "https://pub.dev"
source: hosted
version: "1.0.0+7.7.0"
dart_mappable: dart_mappable:
dependency: "direct main" dependency: "direct main"
description: description:
name: dart_mappable name: dart_mappable
sha256: "6eda273146ed930c1f161d0b29f4bc9ef9e87ecfb9341607833bf76b008fb7d5" sha256: "2255b2c00e328a65fef5a8df2dabfc0dc9c2e518c33a50051a4519b1c7a28c48"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.3.1" version: "4.5.0"
dart_mappable_builder: dart_mappable_builder:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: dart_mappable_builder name: dart_mappable_builder
sha256: b3673a6d190f2ea766b39ea298d4c55d1caca9382a536cf164ffe7e2f955c501 sha256: adea8c55aac73c8254aa14a8272b788eb0f72799dd8e4810a9b664ec9b4e353c
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.3.1+1" version: "4.5.0"
dart_style: dart_style:
dependency: transitive dependency: transitive
description: description:
name: dart_style name: dart_style
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.6" version: "3.0.1"
dartx: dartx:
dependency: "direct main" dependency: "direct main"
description: description:
@ -381,18 +389,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: drift name: drift
sha256: df027d168a2985a2e9da900adeba2ab0136f0d84436592cf3cd5135f82c8579c sha256: "540cf382a3bfa99b76e51514db5b0ebcd81ce3679b7c1c9cb9478ff3735e47a1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.21.0" version: "2.28.2"
drift_dev: drift_dev:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: drift_dev name: drift_dev
sha256: "623649abe932fc17bd32e578e7e05f7ac5e7dd0b33e6c8669a0634105d1389bf" sha256: "68c138e884527d2bd61df2ade276c3a144df84d1adeb0ab8f3196b5afe021bd4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.21.2" version: "2.28.0"
easy_refresh: easy_refresh:
dependency: "direct main" dependency: "direct main"
description: description:
@ -474,18 +482,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_gen_core name: flutter_gen_core
sha256: "53890b653738f34363d9f0d40f82104c261716bd551d3ba65f648770b6764c21" sha256: b6bafbbd981da2f964eb45bcb8b8a7676a281084f8922c0c75de4cfbaa849311
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.9.0" version: "5.12.0"
flutter_gen_runner: flutter_gen_runner:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_gen_runner name: flutter_gen_runner
sha256: de70b42eb5329f712c8b041069d081ad5fb5109f32d6d1ea9c1b39596786215d sha256: c99b10af9d404e3f46fd1927e7d90099779e935e86022674c4c2a9e6c2a93b29
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.9.0" version: "5.12.0"
flutter_hooks: flutter_hooks:
dependency: transitive dependency: transitive
description: description:
@ -652,10 +660,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: freezed name: freezed
sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 sha256: "59a584c24b3acdc5250bb856d0d3e9c0b798ed14a4af1ddb7dc1c7b41df91c9c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.2" version: "2.5.8"
freezed_annotation: freezed_annotation:
dependency: "direct main" dependency: "direct main"
description: description:
@ -692,10 +700,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: go_router_builder name: go_router_builder
sha256: "3425b72dea69209754ac6b71b4da34165dcd4d4a2934713029945709a246427a" sha256: "7f6f4bfb97cadc3d25378a0237fe4ddd98b54d6094b5a5c158b775a2cc30843e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.7.1" version: "2.9.0"
google_identity_services_web: google_identity_services_web:
dependency: transitive dependency: transitive
description: description:
@ -724,10 +732,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: grpc name: grpc
sha256: e93ee3bce45c134bf44e9728119102358c7cd69de7832d9a874e2e74eb8cab40 sha256: "2dde469ddd8bbd7a33a0765da417abe1ad2142813efce3a86c512041294e2b26"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.4" version: "4.1.0"
hashcodes: hashcodes:
dependency: transitive dependency: transitive
description: description:
@ -808,6 +816,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
image:
dependency: transitive
description:
name: image
sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c"
url: "https://pub.dev"
source: hosted
version: "4.7.2"
image_size_getter: image_size_getter:
dependency: transitive dependency: transitive
description: description:
@ -908,10 +924,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: json_serializable name: json_serializable
sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.8.0" version: "6.9.5"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@ -1208,6 +1224,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.2" version: "1.5.2"
posix:
dependency: transitive
description:
name: posix
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
protobuf: protobuf:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1292,10 +1316,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: riverpod_analyzer_utils name: riverpod_analyzer_utils
sha256: "8b71f03fc47ae27d13769496a1746332df4cec43918aeba9aff1e232783a780f" sha256: "837a6dc33f490706c7f4632c516bcd10804ee4d9ccc8046124ca56388715fdf3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.1" version: "0.5.9"
riverpod_annotation: riverpod_annotation:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1308,10 +1332,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: riverpod_generator name: riverpod_generator
sha256: d451608bf17a372025fc36058863737636625dfdb7e3cbf6142e0dfeb366ab22 sha256: "120d3310f687f43e7011bb213b90a436f1bbc300f0e4b251a72c39bccb017a4f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.0" version: "2.6.4"
rxdart: rxdart:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1433,10 +1457,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: source_gen name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.0" version: "2.0.0"
source_helper: source_helper:
dependency: transitive dependency: transitive
description: description:
@ -1473,10 +1497,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: sqlparser name: sqlparser
sha256: d77749237609784e337ec36c979d41f6f38a7b279df98622ae23929c8eb954a4 sha256: "57090342af1ce32bb499aa641f4ecdd2d6231b9403cea537ac059e803cc20d67"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.39.2" version: "0.41.2"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -1725,38 +1749,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.3" version: "3.0.3"
webview_flutter:
dependency: "direct main"
description:
name: webview_flutter
sha256: "889a0a678e7c793c308c68739996227c9661590605e70b1f6cf6b9a6634f7aec"
url: "https://pub.dev"
source: hosted
version: "4.10.0"
webview_flutter_android:
dependency: transitive
description:
name: webview_flutter_android
sha256: "512c26ccc5b8a571fd5d13ec994b7509f142ff6faf85835e243dde3538fdc713"
url: "https://pub.dev"
source: hosted
version: "4.3.2"
webview_flutter_platform_interface:
dependency: transitive
description:
name: webview_flutter_platform_interface
sha256: "7cb32b21825bd65569665c32bb00a34ded5779786d6201f5350979d2d529940d"
url: "https://pub.dev"
source: hosted
version: "2.13.0"
webview_flutter_wkwebview:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: a3d461fe3467014e05f3ac4962e5fdde2a4bf44c561cb53e9ae5c586600fdbc3
url: "https://pub.dev"
source: hosted
version: "3.22.0"
win32: win32:
dependency: transitive dependency: transitive
description: description:

View File

@ -49,7 +49,7 @@ dependencies:
# 网络和数据处理 # 网络和数据处理
dio: ^5.4.1 dio: ^5.4.1
grpc: ^3.2.4 grpc: ^4.1.0
protobuf: ^3.1.0 protobuf: ^3.1.0
json_annotation: ^4.9.0 json_annotation: ^4.9.0
dart_mappable: ^4.2.1 dart_mappable: ^4.2.1
@ -71,7 +71,6 @@ dependencies:
# 平台集成 # 平台集成
window_manager: ^0.4.3 window_manager: ^0.4.3
webview_flutter: ^4.7.0
url_launcher: ^6.3.1 url_launcher: ^6.3.1
flutter_inappwebview: ^6.1.5 # 最新稳定版本 flutter_inappwebview: ^6.1.5 # 最新稳定版本
crisp_sdk: ^1.1.0 # 使用 crisp_sdk配合最新的 flutter_inappwebview crisp_sdk: ^1.1.0 # 使用 crisp_sdk配合最新的 flutter_inappwebview
@ -129,7 +128,7 @@ dev_dependencies:
drift_dev: ^2.16.0 drift_dev: ^2.16.0
ffigen: ^8.0.2 ffigen: ^8.0.2
slang_build_runner: ^3.30.0 slang_build_runner: ^3.30.0
flutter_gen_runner: ^5.4.0 flutter_gen_runner: ^5.12.0
go_router_builder: ^2.4.1 go_router_builder: ^2.4.1
dart_mappable_builder: ^4.2.1 dart_mappable_builder: ^4.2.1