feat: 新增allinssl暗色模式配置-黑金主题
@@ -11727,5 +11727,22 @@
|
||||
"arDZ": "الهيئة الخاصة"
|
||||
},
|
||||
"timestamp": "2025-08-26T10:10:20.746Z"
|
||||
},
|
||||
"请输入证书名称搜索": {
|
||||
"text": "请输入证书名称搜索",
|
||||
"key": "t_0_1763542847861",
|
||||
"translations": {
|
||||
"zhCN": "请输入证书名称搜索",
|
||||
"zhTW": "請輸入憑證名稱搜尋",
|
||||
"enUS": "Please enter the certificate name to search",
|
||||
"jaJP": "証明書の名前を入力して検索してください",
|
||||
"koKR": "인증서 이름을 입력하여 검색하세요",
|
||||
"ruRU": "Введите название сертификата для поиска",
|
||||
"ptBR": "Por favor, insira o nome do certificado para pesquisar",
|
||||
"frFR": "Veuillez entrer le nom du certificat pour rechercher",
|
||||
"esAR": "Ingrese el nombre del certificado para buscar",
|
||||
"arDZ": "الرجاء إدخال اسم الشهادة للبحث"
|
||||
},
|
||||
"timestamp": "2025-11-19T09:00:47.861Z"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1413px" height="338px" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g><path style="opacity:0.962" fill="#f8a11c" d="M 131.5,5.5 C 137.682,4.71925 142.182,7.05258 145,12.5C 145.667,26.8333 145.667,41.1667 145,55.5C 138.784,64.4081 132.117,64.7415 125,56.5C 124.333,41.5 124.333,26.5 125,11.5C 126.494,8.68648 128.66,6.68648 131.5,5.5 Z"/></g>
|
||||
<g><path style="opacity:0.938" fill="#f8a11c" d="M 40.5,42.5 C 44.3076,42.1632 47.9743,42.6632 51.5,44C 61.9591,52.1236 72.1257,60.6236 82,69.5C 85.5704,78.6939 82.5704,84.3605 73,86.5C 70.3658,86.0664 67.8658,85.233 65.5,84C 55.6667,75.5 45.8333,67 36,58.5C 33.1587,51.8715 34.6587,46.5382 40.5,42.5 Z"/></g>
|
||||
<g><path style="opacity:0.942" fill="#f8a11c" d="M 219.5,42.5 C 232.389,41.553 236.889,47.2196 233,59.5C 223.126,68.3764 212.959,76.8764 202.5,85C 191.172,87.675 186.005,83.175 187,71.5C 187.5,70.5 188,69.5 188.5,68.5C 198.878,59.7807 209.211,51.1141 219.5,42.5 Z"/></g>
|
||||
<g><path style="opacity:0.975" fill="#f8a01d" d="M 193.5,155.5 C 182.167,156.833 170.833,156.833 159.5,155.5C 159.666,148.825 159.5,142.158 159,135.5C 153.087,116.64 140.92,110.806 122.5,118C 116.571,122.02 112.738,127.52 111,134.5C 110.5,141.492 110.334,148.492 110.5,155.5C 98.8333,156.833 87.1667,156.833 75.5,155.5C 72.0122,130.118 79.6789,108.951 98.5,92C 118.363,78.9872 139.363,76.9872 161.5,86C 187.348,101.878 198.015,125.045 193.5,155.5 Z M 159.5,155.5 C 143.167,155.5 126.833,155.5 110.5,155.5C 110.334,148.492 110.5,141.492 111,134.5C 112.738,127.52 116.571,122.02 122.5,118C 140.92,110.806 153.087,116.64 159,135.5C 159.5,142.158 159.666,148.825 159.5,155.5 Z"/></g>
|
||||
<g><path style="opacity:0.966" fill="#f8a11c" d="M 9.5,119.5 C 23.8372,119.333 38.1705,119.5 52.5,120C 55,121.167 56.8333,123 58,125.5C 60.067,131.987 58.2337,136.82 52.5,140C 37.8333,140.667 23.1667,140.667 8.5,140C 0.496354,132.82 0.829687,125.987 9.5,119.5 Z"/></g>
|
||||
<g><path style="opacity:0.966" fill="#f8a11c" d="M 217.5,119.5 C 231.504,119.333 245.504,119.5 259.5,120C 266.053,122.73 268.22,127.563 266,134.5C 264.833,137 263,138.833 260.5,140C 245.833,140.667 231.167,140.667 216.5,140C 209.938,135.401 208.771,129.568 213,122.5C 214.652,121.601 216.152,120.601 217.5,119.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 601.5,152.5 C 609.624,152.31 615.79,155.644 620,162.5C 625.113,181.855 619.28,196.688 602.5,207C 598.312,208.631 593.979,209.464 589.5,209.5C 589.5,204.5 589.5,199.5 589.5,194.5C 596.098,193.906 600.765,190.573 603.5,184.5C 589.406,182.218 584.572,174.218 589,160.5C 590.833,158.667 592.667,156.833 594.5,155C 597.004,154.302 599.337,153.469 601.5,152.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 75.5,155.5 C 87.1667,156.833 98.8333,156.833 110.5,155.5C 126.833,155.5 143.167,155.5 159.5,155.5C 170.833,156.833 182.167,156.833 193.5,155.5C 198.844,155.334 204.177,155.501 209.5,156C 213.68,157.002 216.514,159.502 218,163.5C 218.982,202.56 218.649,241.56 217,280.5C 215.457,282.378 213.624,283.878 211.5,285C 160.5,285.667 109.5,285.667 58.5,285C 55.6667,283.5 53.5,281.333 52,278.5C 51.3333,239.833 51.3333,201.167 52,162.5C 53.668,158.835 56.5013,156.668 60.5,156C 65.4889,155.501 70.4889,155.334 75.5,155.5 Z M 130.5,199.5 C 145.643,199.162 151.143,206.162 147,220.5C 146,222.833 144.333,224.5 142,225.5C 141.667,231.167 141.333,236.833 141,242.5C 137.509,248.661 133.509,248.994 129,243.5C 128.83,237.472 128.33,231.472 127.5,225.5C 120.327,220.137 118.827,213.471 123,205.5C 125.134,202.847 127.634,200.847 130.5,199.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 318.5,155.5 C 329.5,155.5 340.5,155.5 351.5,155.5C 351.5,189.833 351.5,224.167 351.5,258.5C 368.167,258.5 384.833,258.5 401.5,258.5C 401.5,268.167 401.5,277.833 401.5,287.5C 373.833,287.5 346.167,287.5 318.5,287.5C 318.5,243.5 318.5,199.5 318.5,155.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 747.5,155.5 C 777.167,155.5 806.833,155.5 836.5,155.5C 836.5,164.833 836.5,174.167 836.5,183.5C 817.167,183.5 797.833,183.5 778.5,183.5C 778.5,191.167 778.5,198.833 778.5,206.5C 796.833,206.5 815.167,206.5 833.5,206.5C 833.5,215.5 833.5,224.5 833.5,233.5C 815.167,233.5 796.833,233.5 778.5,233.5C 778.5,242.167 778.5,250.833 778.5,259.5C 798.833,259.5 819.167,259.5 839.5,259.5C 839.5,268.833 839.5,278.167 839.5,287.5C 808.833,287.5 778.167,287.5 747.5,287.5C 747.5,243.5 747.5,199.5 747.5,155.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 524.5,169.5 C 534.833,169.5 545.167,169.5 555.5,169.5C 555.5,177.833 555.5,186.167 555.5,194.5C 562.5,194.5 569.5,194.5 576.5,194.5C 576.5,202.167 576.5,209.833 576.5,217.5C 569.5,217.5 562.5,217.5 555.5,217.5C 555.333,231.171 555.5,244.837 556,258.5C 557.792,263.161 561.292,265.494 566.5,265.5C 569.208,264.959 571.874,264.292 574.5,263.5C 575.498,271.472 575.831,279.472 575.5,287.5C 564.869,289.85 554.202,290.016 543.5,288C 532.555,284.042 526.388,276.209 525,264.5C 524.5,248.837 524.333,233.17 524.5,217.5C 519.5,217.5 514.5,217.5 509.5,217.5C 509.5,209.833 509.5,202.167 509.5,194.5C 514.5,194.5 519.5,194.5 524.5,194.5C 524.5,186.167 524.5,177.833 524.5,169.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 1357.5,169.5 C 1367.83,169.5 1378.17,169.5 1388.5,169.5C 1388.5,177.833 1388.5,186.167 1388.5,194.5C 1395.83,194.5 1403.17,194.5 1410.5,194.5C 1410.5,202.167 1410.5,209.833 1410.5,217.5C 1403.17,217.5 1395.83,217.5 1388.5,217.5C 1388.33,230.837 1388.5,244.171 1389,257.5C 1390.61,262.605 1394.11,265.271 1399.5,265.5C 1402.21,264.959 1404.87,264.292 1407.5,263.5C 1409.25,271.347 1409.59,279.181 1408.5,287C 1394.73,291.136 1381.4,290.136 1368.5,284C 1361.53,277.783 1358.03,269.95 1358,260.5C 1357.5,246.171 1357.33,231.837 1357.5,217.5C 1352.83,217.5 1348.17,217.5 1343.5,217.5C 1343.5,209.833 1343.5,202.167 1343.5,194.5C 1348.17,194.5 1352.83,194.5 1357.5,194.5C 1357.5,186.167 1357.5,177.833 1357.5,169.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 450.5,191.5 C 481.293,188.162 499.793,201.495 506,231.5C 506.499,237.491 506.666,243.491 506.5,249.5C 484.164,249.333 461.831,249.5 439.5,250C 444.872,263.187 454.538,268.187 468.5,265C 473.758,263.574 478.092,260.741 481.5,256.5C 489,260.749 496.334,265.249 503.5,270C 492.992,284.169 478.825,291.003 461,290.5C 425.452,288.622 408.452,269.955 410,234.5C 413.465,210.539 426.965,196.206 450.5,191.5 Z M 454.5,213.5 C 467.671,211.233 475.338,216.566 477.5,229.5C 464.829,229.667 452.162,229.5 439.5,229C 442.018,221.317 447.018,216.15 454.5,213.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 644.5,191.5 C 657.321,190.255 669.654,192.088 681.5,197C 685.282,198.954 688.615,201.454 691.5,204.5C 686.537,211.131 680.87,217.131 674.5,222.5C 670.461,219.481 666.128,216.981 661.5,215C 657.5,214.333 653.5,214.333 649.5,215C 643.81,217.782 643.143,221.449 647.5,226C 658.652,228.273 669.319,231.94 679.5,237C 691.608,248.015 693.774,260.848 686,275.5C 674.103,287.774 659.603,292.608 642.5,290C 629.937,288.881 618.937,284.215 609.5,276C 614.983,269.35 620.983,263.183 627.5,257.5C 634.692,264.73 643.358,267.897 653.5,267C 660.847,264.938 662.18,260.938 657.5,255C 649.32,251.871 640.987,249.205 632.5,247C 618.729,240.962 613.229,230.462 616,215.5C 617.834,207.998 622.001,202.165 628.5,198C 633.603,194.938 638.936,192.771 644.5,191.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 910.5,191.5 C 933.705,189.879 946.872,200.545 950,223.5C 950.5,244.831 950.667,266.164 950.5,287.5C 939.833,287.5 929.167,287.5 918.5,287.5C 918.667,267.831 918.5,248.164 918,228.5C 914.428,218.082 907.595,215.249 897.5,220C 894.131,222.365 891.964,225.532 891,229.5C 889.564,248.787 889.064,268.12 889.5,287.5C 879.167,287.5 868.833,287.5 858.5,287.5C 858.5,256.5 858.5,225.5 858.5,194.5C 868.5,194.5 878.5,194.5 888.5,194.5C 888.335,198.182 888.501,201.848 889,205.5C 894.479,198.181 901.645,193.515 910.5,191.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 1001.5,191.5 C 1014.5,190.001 1026.84,192.168 1038.5,198C 1041.19,199.698 1043.52,201.698 1045.5,204C 1039.79,211.24 1033.96,218.407 1028,225.5C 1017.28,214.846 1006.61,214.846 996,225.5C 988.734,237.131 989.567,248.298 998.5,259C 1009.29,265.565 1019.45,264.732 1029,256.5C 1034.33,263.167 1039.67,269.833 1045,276.5C 1045.71,278.367 1045.21,279.867 1043.5,281C 1021.83,293.667 1000.17,293.667 978.5,281C 962.28,268.02 956.113,251.187 960,230.5C 965.313,208.352 979.146,195.352 1001.5,191.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 1106.5,191.5 C 1110.24,191.241 1113.91,191.574 1117.5,192.5C 1118.27,201.836 1117.93,211.17 1116.5,220.5C 1101.47,217.091 1091.97,222.758 1088,237.5C 1087.5,254.163 1087.33,270.83 1087.5,287.5C 1077.17,287.5 1066.83,287.5 1056.5,287.5C 1056.5,256.5 1056.5,225.5 1056.5,194.5C 1066.5,194.5 1076.5,194.5 1086.5,194.5C 1086.33,198.182 1086.5,201.848 1087,205.5C 1091.76,198.222 1098.26,193.556 1106.5,191.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 1289.5,191.5 C 1321.74,191.732 1338.74,208.065 1340.5,240.5C 1340.2,261.627 1330.87,277.127 1312.5,287C 1300.72,291.528 1289.06,291.194 1277.5,286C 1273.76,284.262 1270.59,281.762 1268,278.5C 1267.5,296.164 1267.33,313.83 1267.5,331.5C 1257.17,331.5 1246.83,331.5 1236.5,331.5C 1236.5,285.833 1236.5,240.167 1236.5,194.5C 1246.5,194.5 1256.5,194.5 1266.5,194.5C 1266.33,197.85 1266.5,201.183 1267,204.5C 1273.17,197.673 1280.67,193.339 1289.5,191.5 Z M 1283.5,217.5 C 1302.24,217.405 1310.74,226.738 1309,245.5C 1303.81,262.899 1292.97,267.733 1276.5,260C 1263.56,247.638 1263.22,234.971 1275.5,222C 1278.1,220.205 1280.77,218.705 1283.5,217.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#fefffe" d="M 1123.5,194.5 C 1135.19,194.168 1146.85,194.501 1158.5,195.5C 1165.1,214.129 1171.6,232.796 1178,251.5C 1184.38,233.012 1190.38,214.345 1196,195.5C 1207.11,194.18 1218.27,194.18 1229.5,195.5C 1214.9,232.794 1200.4,270.127 1186,307.5C 1181.11,320.357 1171.95,328.191 1158.5,331C 1149.45,331.846 1140.45,331.513 1131.5,330C 1130.9,328.938 1130.57,327.772 1130.5,326.5C 1131.59,318.837 1132.59,311.171 1133.5,303.5C 1140.39,305.314 1147.06,304.814 1153.5,302C 1157.1,296.969 1159.77,291.469 1161.5,285.5C 1148.83,255.148 1136.16,224.815 1123.5,194.5 Z"/></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
BIN
frontend/apps/allin-ssl/public/static/icons/letsencrypt-icon.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 2258.5 538.1" style="enable-background:new 0 0 2258.5 538.1;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#00AF73;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M2083.3,123.5c0-10.6,2.6-20.5,7.8-29.6s12.4-16.3,21.6-21.5c9.2-5.2,19.1-7.9,29.8-7.9s20.6,2.6,29.7,7.9
|
||||
c9.2,5.2,16.3,12.4,21.5,21.5s7.8,19,7.8,29.6c0,10.4-2.5,20.1-7.5,29.1c-5,9.1-12.1,16.3-21.3,21.8s-19.3,8.3-30.3,8.3
|
||||
c-10.9,0-21-2.7-30.2-8.2c-9.2-5.4-16.3-12.7-21.4-21.8C2085.9,143.8,2083.3,134,2083.3,123.5z M2092.9,123.5
|
||||
c0,9,2.2,17.4,6.7,25.1c4.4,7.7,10.5,13.7,18.1,18.1c7.6,4.4,15.9,6.5,24.8,6.5c8.9,0,17.2-2.2,24.9-6.7s13.7-10.5,18-18.1
|
||||
c4.4-7.6,6.5-16,6.5-25s-2.2-17.3-6.5-24.9c-4.4-7.6-10.4-13.6-18-18s-16-6.6-24.9-6.6s-17.2,2.2-24.8,6.5
|
||||
c-7.7,4.3-13.7,10.4-18.1,18C2095.1,106.2,2092.9,114.5,2092.9,123.5z M2130.2,129.4v26.7h-11.7v-67h23c8.5,0,15.1,1.7,19.8,5.2
|
||||
c4.7,3.4,7,8.5,7,15s-3.6,11.4-10.7,14.8c6.8,2.6,10.2,8,10.2,16.1v4.8c0,4.5,0.5,7.8,1.4,9.8v1.3h-12.1
|
||||
c-0.8-1.9-1.2-5.3-1.2-10.1c0-4.9-0.1-7.8-0.2-8.6c-0.9-5.1-4.5-7.7-10.9-7.9h-14.6V129.4z M2130.2,119.3h13c4-0.1,7.2-1,9.7-2.8
|
||||
c2.4-1.7,3.7-4,3.7-6.8c0-3.8-1.1-6.5-3.2-8.1c-2.2-1.6-6-2.4-11.6-2.4h-11.5L2130.2,119.3L2130.2,119.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M365.2,83.8c5.7-8.5,0.5-19.3-9.4-19.3H266h-40h-29.4c-33.5,0-60.2,8.9-79.9,26.7
|
||||
c-19.7,17.7-31,43.1-34,76.3l0,0l-7.6,86.8l0,0c-2.8,33.2,4.1,58.6,20.7,76.3c16.7,17.8,41.8,26.7,75.3,26.7h36.4v-0.2
|
||||
c7.2-1,11.1-5.4,11.8-13.1l3.7-42.6c0.7-7.7-5-14-12.8-14H190c-13.1,0-22.3-2.9-27.6-8.6c-5.2-5.7-7.2-15.4-6.1-29l6.8-77.8
|
||||
c1.2-13.6,4.9-23.2,11.1-28.9c6.2-5.7,15.9-8.6,29.1-8.6h16.6h26.6h64.2c12.7,0,25-6.7,32.3-17.7L365.2,83.8z"/>
|
||||
<path class="st1" d="M59.3,454.3c-5.7,8.5-0.5,19.3,9.4,19.3h92.1h40h29.4c33.5,0,60.2-8.9,79.9-26.7c19.7-17.7,31-43.1,34-76.3
|
||||
l0,0l7.6-86.8l0,0c2.8-33.2-4.1-58.6-20.7-76.3c-16.7-17.8-41.8-26.7-75.3-26.7h-36.4v0.2c-7.2,1-11.1,5.4-11.8,13.1l-3.7,42.6
|
||||
c-0.7,7.7,5,14,12.8,14H237c13.1,0,22.3,2.9,27.6,8.6c5.2,5.7,7.2,15.4,6.1,28.9l-6.8,77.8c-1.2,13.6-4.9,23.2-11.1,28.9
|
||||
c-6.2,5.7-15.9,8.6-29.1,8.6H207h-26.6h-66.5c-12.7,0-25,6.7-32.3,17.7L59.3,454.3z"/>
|
||||
</g>
|
||||
<path class="st0" d="M607,473.6H399c-8.9,0-13-4.4-12.2-13.4l33.5-382.3c0.8-8.9,5.6-13.4,14.5-13.4h208c8.5,0,12.3,4.4,11.6,13.4
|
||||
l-3.8,43.3c-0.8,8.9-5.4,13.4-13.9,13.4h-133c-5.1,0-7.8,2.3-8.2,7l-7.2,82.7c-0.4,4.7,1.9,7,7,7h108.8c8.9,0,13,4.5,12.2,13.4
|
||||
l-3.8,43.3c-0.8,8.9-5.6,13.4-14.5,13.4H489.2c-5.1,0-7.8,2.3-8.2,7l-7.7,88.4c-0.4,4.7,1.9,7,7,7h132.9c8.5,0,12.3,4.5,11.6,13.4
|
||||
l-3.8,43.3C620.1,469.1,615.5,473.6,607,473.6"/>
|
||||
<path class="st0" d="M831.4,473.6h-69.3c-33.5,0-58.6-8.9-75.3-26.7c-16.7-17.8-23.6-43.5-20.6-77l17.6-201.7
|
||||
c2.9-33.5,14.3-59.2,34.1-77s46.4-26.7,79.9-26.7h69.3c33.1,0,58.1,9,74.9,27c16.9,18,23.9,43.6,21,76.6l-2.6,29.9
|
||||
c-0.8,9.3-5.9,14-15.2,14h-53.4c-8.9,0-13-4.7-12.1-14l2.3-26.1c1.2-13.6-0.8-23.2-6.1-28.9c-5.2-5.7-14.6-8.6-28.2-8.6h-43.3
|
||||
c-13.1,0-22.8,2.9-29.1,8.6c-6.2,5.7-9.9,15.4-11.1,28.9l-17,194c-1.2,13.6,0.8,23.2,6.1,28.9c5.2,5.7,14.4,8.6,27.6,8.6h43.3
|
||||
c13.6,0,23.5-2.9,29.7-8.6s9.9-15.4,11.1-28.9l2.3-26.1c0.8-9.3,5.7-14,14.6-14h53.4c9.3,0,13.6,4.7,12.8,14l-2.6,29.9
|
||||
c-2.9,33.1-14.4,58.6-34.4,76.6C891,464.6,864.5,473.6,831.4,473.6"/>
|
||||
<path class="st0" d="M1127,473.6h-53.4c-9.3,0-13.6-4.4-12.8-13.4l27.9-318.7c0.4-4.7-1.7-7-6.4-7h-72.5c-8.9,0-13-4.4-12.2-13.4
|
||||
l3.8-43.3c0.8-8.9,5.6-13.4,14.5-13.4h240.4c8.9,0,13,4.4,12.2,13.4l-3.8,43.3c-0.8,8.9-5.6,13.4-14.5,13.4h-72.5
|
||||
c-5.1,0-7.8,2.3-8.2,7l-27.9,318.7C1140.7,469.1,1135.9,473.6,1127,473.6"/>
|
||||
<path class="st0" d="M1390.5,77.9L1357,460.2c-0.8,8.9-5.6,13.4-14.5,13.4h-54.1c-8.9,0-13-4.5-12.2-13.4l33.5-382.3
|
||||
c0.8-8.9,5.6-13.4,14.5-13.4h54.1C1387.2,64.5,1391.3,69,1390.5,77.9"/>
|
||||
<path class="st0" d="M1857.4,403.6h45.2c13.6,0,23.5-2.9,29.7-8.6s9.9-15.4,11.1-28.9l17-194c1.2-13.6-0.8-23.2-6.1-28.9
|
||||
c-5.2-5.7-14.6-8.6-28.2-8.6h-45.2c-13.6,0-23.4,2.9-29.4,8.6c-6,5.7-9.6,15.4-10.8,28.9l-17,194c-1.2,13.6,0.7,23.2,5.7,28.9
|
||||
C1834.6,400.7,1843.8,403.6,1857.4,403.6 M1909.8,473.6h-71.2c-33.5,0-58.6-8.9-75.3-26.7c-16.7-17.8-23.6-43.5-20.6-77
|
||||
l17.6-201.7c2.9-33.5,14.3-59.2,34.1-77c19.8-17.8,46.4-26.7,79.9-26.7h71.2c33.5,0,58.6,8.9,75.3,26.7
|
||||
c16.7,17.8,23.6,43.5,20.6,77l-17.6,201.7c-2.9,33.5-14.3,59.2-34.1,77C1970,464.7,1943.3,473.6,1909.8,473.6"/>
|
||||
<path class="st0" d="M1639.5,201.3h54.1c8.9,0,13.8-4.7,14.6-14l1.7-19.1c2.9-33.1-4-58.6-20.6-76.7c-16.7-18-41.5-27-74.6-27
|
||||
h-67.4c-33.5,0-60.2,8.9-79.9,26.7c-19.8,17.8-31.2,43.5-34.1,77l-17.6,201.7c-2.9,33.5,3.9,59.2,20.6,77s41.8,26.7,75.3,26.7
|
||||
h67.4c33.1,0,59.5-9,79.3-27s31.2-43.6,34.1-76.7l9.3-106.2c0.9-9.8-3.6-14.6-13.4-14.6H1577h-14.6c-9.9,0-17,10.7-12.8,19.3
|
||||
l16.4,33c5.4,10.9,16.6,17.7,29.2,17.7h13.8c4.7,0,6.8,2.3,6.4,7l-3.5,40.1c-1.2,13.6-4.8,23.2-10.8,29c-6,5.7-15.8,8.6-29.4,8.6
|
||||
h-41.4c-13.1,0-22.3-2.9-27.6-8.6c-5.2-5.7-7.2-15.4-6.1-29l17-194c1.2-13.6,4.9-23.2,11.1-28.9s15.9-8.6,29.1-8.6h41.4
|
||||
c13.6,0,22.9,2.9,27.9,8.6c5,5.7,6.9,15.4,5.7,28.9l-1.3,15.3C1626.5,196.6,1630.6,201.3,1639.5,201.3"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
60
frontend/apps/allin-ssl/public/static/icons/sectigo-ico.svg
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 2258.5 538.1" style="enable-background:new 0 0 2258.5 538.1;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#091B2A;}
|
||||
.st1{fill:#00AF73;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M2083.3,123.5c0-10.6,2.6-20.5,7.8-29.6s12.4-16.3,21.6-21.5c9.2-5.2,19.1-7.9,29.8-7.9s20.6,2.6,29.7,7.9
|
||||
c9.2,5.2,16.3,12.4,21.5,21.5s7.8,19,7.8,29.6c0,10.4-2.5,20.1-7.5,29.1c-5,9.1-12.1,16.3-21.3,21.8s-19.3,8.3-30.3,8.3
|
||||
c-10.9,0-21-2.7-30.2-8.2c-9.2-5.4-16.3-12.7-21.4-21.8C2085.9,143.8,2083.3,134,2083.3,123.5z M2092.9,123.5
|
||||
c0,9,2.2,17.4,6.7,25.1c4.4,7.7,10.5,13.7,18.1,18.1c7.6,4.4,15.9,6.5,24.8,6.5c8.9,0,17.2-2.2,24.9-6.7s13.7-10.5,18-18.1
|
||||
c4.4-7.6,6.5-16,6.5-25s-2.2-17.3-6.5-24.9c-4.4-7.6-10.4-13.6-18-18s-16-6.6-24.9-6.6s-17.2,2.2-24.8,6.5
|
||||
c-7.7,4.3-13.7,10.4-18.1,18C2095.1,106.2,2092.9,114.5,2092.9,123.5z M2130.2,129.4v26.7h-11.7v-67h23c8.5,0,15.1,1.7,19.8,5.2
|
||||
c4.7,3.4,7,8.5,7,15s-3.6,11.4-10.7,14.8c6.8,2.6,10.2,8,10.2,16.1v4.8c0,4.5,0.5,7.8,1.4,9.8v1.3h-12.1
|
||||
c-0.8-1.9-1.2-5.3-1.2-10.1c0-4.9-0.1-7.8-0.2-8.6c-0.9-5.1-4.5-7.7-10.9-7.9h-14.6V129.4z M2130.2,119.3h13c4-0.1,7.2-1,9.7-2.8
|
||||
c2.4-1.7,3.7-4,3.7-6.8c0-3.8-1.1-6.5-3.2-8.1c-2.2-1.6-6-2.4-11.6-2.4h-11.5L2130.2,119.3L2130.2,119.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M365.2,83.8c5.7-8.5,0.5-19.3-9.4-19.3H266h-40h-29.4c-33.5,0-60.2,8.9-79.9,26.7
|
||||
c-19.7,17.7-31,43.1-34,76.3l0,0l-7.6,86.8l0,0c-2.8,33.2,4.1,58.6,20.7,76.3c16.7,17.8,41.8,26.7,75.3,26.7h36.4v-0.2
|
||||
c7.2-1,11.1-5.4,11.8-13.1l3.7-42.6c0.7-7.7-5-14-12.8-14H190c-13.1,0-22.3-2.9-27.6-8.6c-5.2-5.7-7.2-15.4-6.1-29l6.8-77.8
|
||||
c1.2-13.6,4.9-23.2,11.1-28.9c6.2-5.7,15.9-8.6,29.1-8.6h16.6h26.6h64.2c12.7,0,25-6.7,32.3-17.7L365.2,83.8z"/>
|
||||
<path class="st1" d="M59.3,454.3c-5.7,8.5-0.5,19.3,9.4,19.3h92.1h40h29.4c33.5,0,60.2-8.9,79.9-26.7c19.7-17.7,31-43.1,34-76.3
|
||||
l0,0l7.6-86.8l0,0c2.8-33.2-4.1-58.6-20.7-76.3c-16.7-17.8-41.8-26.7-75.3-26.7h-36.4v0.2c-7.2,1-11.1,5.4-11.8,13.1l-3.7,42.6
|
||||
c-0.7,7.7,5,14,12.8,14H237c13.1,0,22.3,2.9,27.6,8.6c5.2,5.7,7.2,15.4,6.1,28.9l-6.8,77.8c-1.2,13.6-4.9,23.2-11.1,28.9
|
||||
c-6.2,5.7-15.9,8.6-29.1,8.6H207h-26.6h-66.5c-12.7,0-25,6.7-32.3,17.7L59.3,454.3z"/>
|
||||
</g>
|
||||
<path class="st0" d="M607,473.6H399c-8.9,0-13-4.4-12.2-13.4l33.5-382.3c0.8-8.9,5.6-13.4,14.5-13.4h208c8.5,0,12.3,4.4,11.6,13.4
|
||||
l-3.8,43.3c-0.8,8.9-5.4,13.4-13.9,13.4h-133c-5.1,0-7.8,2.3-8.2,7l-7.2,82.7c-0.4,4.7,1.9,7,7,7h108.8c8.9,0,13,4.5,12.2,13.4
|
||||
l-3.8,43.3c-0.8,8.9-5.6,13.4-14.5,13.4H489.2c-5.1,0-7.8,2.3-8.2,7l-7.7,88.4c-0.4,4.7,1.9,7,7,7h132.9c8.5,0,12.3,4.5,11.6,13.4
|
||||
l-3.8,43.3C620.1,469.1,615.5,473.6,607,473.6"/>
|
||||
<path class="st0" d="M831.4,473.6h-69.3c-33.5,0-58.6-8.9-75.3-26.7c-16.7-17.8-23.6-43.5-20.6-77l17.6-201.7
|
||||
c2.9-33.5,14.3-59.2,34.1-77s46.4-26.7,79.9-26.7h69.3c33.1,0,58.1,9,74.9,27c16.9,18,23.9,43.6,21,76.6l-2.6,29.9
|
||||
c-0.8,9.3-5.9,14-15.2,14h-53.4c-8.9,0-13-4.7-12.1-14l2.3-26.1c1.2-13.6-0.8-23.2-6.1-28.9c-5.2-5.7-14.6-8.6-28.2-8.6h-43.3
|
||||
c-13.1,0-22.8,2.9-29.1,8.6c-6.2,5.7-9.9,15.4-11.1,28.9l-17,194c-1.2,13.6,0.8,23.2,6.1,28.9c5.2,5.7,14.4,8.6,27.6,8.6h43.3
|
||||
c13.6,0,23.5-2.9,29.7-8.6s9.9-15.4,11.1-28.9l2.3-26.1c0.8-9.3,5.7-14,14.6-14h53.4c9.3,0,13.6,4.7,12.8,14l-2.6,29.9
|
||||
c-2.9,33.1-14.4,58.6-34.4,76.6C891,464.6,864.5,473.6,831.4,473.6"/>
|
||||
<path class="st0" d="M1127,473.6h-53.4c-9.3,0-13.6-4.4-12.8-13.4l27.9-318.7c0.4-4.7-1.7-7-6.4-7h-72.5c-8.9,0-13-4.4-12.2-13.4
|
||||
l3.8-43.3c0.8-8.9,5.6-13.4,14.5-13.4h240.4c8.9,0,13,4.4,12.2,13.4l-3.8,43.3c-0.8,8.9-5.6,13.4-14.5,13.4h-72.5
|
||||
c-5.1,0-7.8,2.3-8.2,7l-27.9,318.7C1140.7,469.1,1135.9,473.6,1127,473.6"/>
|
||||
<path class="st0" d="M1390.5,77.9L1357,460.2c-0.8,8.9-5.6,13.4-14.5,13.4h-54.1c-8.9,0-13-4.5-12.2-13.4l33.5-382.3
|
||||
c0.8-8.9,5.6-13.4,14.5-13.4h54.1C1387.2,64.5,1391.3,69,1390.5,77.9"/>
|
||||
<path class="st0" d="M1857.4,403.6h45.2c13.6,0,23.5-2.9,29.7-8.6s9.9-15.4,11.1-28.9l17-194c1.2-13.6-0.8-23.2-6.1-28.9
|
||||
c-5.2-5.7-14.6-8.6-28.2-8.6h-45.2c-13.6,0-23.4,2.9-29.4,8.6c-6,5.7-9.6,15.4-10.8,28.9l-17,194c-1.2,13.6,0.7,23.2,5.7,28.9
|
||||
C1834.6,400.7,1843.8,403.6,1857.4,403.6 M1909.8,473.6h-71.2c-33.5,0-58.6-8.9-75.3-26.7c-16.7-17.8-23.6-43.5-20.6-77
|
||||
l17.6-201.7c2.9-33.5,14.3-59.2,34.1-77c19.8-17.8,46.4-26.7,79.9-26.7h71.2c33.5,0,58.6,8.9,75.3,26.7
|
||||
c16.7,17.8,23.6,43.5,20.6,77l-17.6,201.7c-2.9,33.5-14.3,59.2-34.1,77C1970,464.7,1943.3,473.6,1909.8,473.6"/>
|
||||
<path class="st0" d="M1639.5,201.3h54.1c8.9,0,13.8-4.7,14.6-14l1.7-19.1c2.9-33.1-4-58.6-20.6-76.7c-16.7-18-41.5-27-74.6-27
|
||||
h-67.4c-33.5,0-60.2,8.9-79.9,26.7c-19.8,17.8-31.2,43.5-34.1,77l-17.6,201.7c-2.9,33.5,3.9,59.2,20.6,77s41.8,26.7,75.3,26.7
|
||||
h67.4c33.1,0,59.5-9,79.3-27s31.2-43.6,34.1-76.7l9.3-106.2c0.9-9.8-3.6-14.6-13.4-14.6H1577h-14.6c-9.9,0-17,10.7-12.8,19.3
|
||||
l16.4,33c5.4,10.9,16.6,17.7,29.2,17.7h13.8c4.7,0,6.8,2.3,6.4,7l-3.5,40.1c-1.2,13.6-4.8,23.2-10.8,29c-6,5.7-15.8,8.6-29.4,8.6
|
||||
h-41.4c-13.1,0-22.3-2.9-27.6-8.6c-5.2-5.7-7.2-15.4-6.1-29l17-194c1.2-13.6,4.9-23.2,11.1-28.9s15.9-8.6,29.1-8.6h41.4
|
||||
c13.6,0,22.9,2.9,27.9,8.6c5,5.7,6.9,15.4,5.7,28.9l-1.3,15.3C1626.5,196.6,1630.6,201.3,1639.5,201.3"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
BIN
frontend/apps/allin-ssl/public/static/icons/ssltrus-ico-dark.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
@@ -0,0 +1,13 @@
|
||||
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16.000000" height="16.000000" fill="none">
|
||||
<rect id="太阳1 1" width="16.000000" height="16.000000" x="0.000000" y="0.000000" />
|
||||
<path id="矢量 668" d="M7.99996 12.3333C10.3932 12.3333 12.3333 10.3932 12.3333 7.99996C12.3333 5.60673 10.3932 3.66663 7.99996 3.66663C5.60673 3.66663 3.66663 5.60673 3.66663 7.99996C3.66663 10.3932 5.60673 12.3333 7.99996 12.3333Z" fill="rgb(255,255,255)" fill-rule="nonzero" />
|
||||
<path id="矢量 668" d="M12.3333 7.99996C12.3333 5.60673 10.3932 3.66663 7.99996 3.66663C5.60673 3.66663 3.66663 5.60673 3.66663 7.99996C3.66663 10.3932 5.60673 12.3333 7.99996 12.3333C10.3932 12.3333 12.3333 10.3932 12.3333 7.99996Z" fill-rule="nonzero" stroke="rgb(255,255,255)" stroke-linejoin="round" stroke-width="1.5" />
|
||||
<path id="矢量 669" d="M7.99996 2.00004C8.46019 2.00004 8.83329 1.62694 8.83329 1.16671C8.83329 0.706471 8.46019 0.333374 7.99996 0.333374C7.53973 0.333374 7.16663 0.706471 7.16663 1.16671C7.16663 1.62694 7.53973 2.00004 7.99996 2.00004Z" fill="rgb(255,255,255)" fill-rule="nonzero" />
|
||||
<path id="矢量 670" d="M12.8333 4.00004C13.2936 4.00004 13.6667 3.62694 13.6667 3.16671C13.6667 2.70647 13.2936 2.33337 12.8333 2.33337C12.3731 2.33337 12 2.70647 12 3.16671C12 3.62694 12.3731 4.00004 12.8333 4.00004Z" fill="rgb(255,255,255)" fill-rule="nonzero" />
|
||||
<path id="矢量 671" d="M14.8333 8.83329C15.2936 8.83329 15.6667 8.46019 15.6667 7.99996C15.6667 7.53973 15.2936 7.16663 14.8333 7.16663C14.3731 7.16663 14 7.53973 14 7.99996C14 8.46019 14.3731 8.83329 14.8333 8.83329Z" fill="rgb(255,255,255)" fill-rule="nonzero" />
|
||||
<path id="矢量 672" d="M12.8333 13.6667C13.2936 13.6667 13.6667 13.2936 13.6667 12.8333C13.6667 12.3731 13.2936 12 12.8333 12C12.3731 12 12 12.3731 12 12.8333C12 13.2936 12.3731 13.6667 12.8333 13.6667Z" fill="rgb(255,255,255)" fill-rule="nonzero" />
|
||||
<path id="矢量 673" d="M7.99996 15.6667C8.46019 15.6667 8.83329 15.2936 8.83329 14.8333C8.83329 14.3731 8.46019 14 7.99996 14C7.53973 14 7.16663 14.3731 7.16663 14.8333C7.16663 15.2936 7.53973 15.6667 7.99996 15.6667Z" fill="rgb(255,255,255)" fill-rule="nonzero" />
|
||||
<path id="矢量 674" d="M3.16671 13.6667C3.62694 13.6667 4.00004 13.2936 4.00004 12.8333C4.00004 12.3731 3.62694 12 3.16671 12C2.70647 12 2.33337 12.3731 2.33337 12.8333C2.33337 13.2936 2.70647 13.6667 3.16671 13.6667Z" fill="rgb(255,255,255)" fill-rule="nonzero" />
|
||||
<path id="矢量 675" d="M1.16671 8.83329C1.62694 8.83329 2.00004 8.46019 2.00004 7.99996C2.00004 7.53973 1.62694 7.16663 1.16671 7.16663C0.706471 7.16663 0.333374 7.53973 0.333374 7.99996C0.333374 8.46019 0.706471 8.83329 1.16671 8.83329Z" fill="rgb(255,255,255)" fill-rule="nonzero" />
|
||||
<path id="矢量 676" d="M3.16671 4.00004C3.62694 4.00004 4.00004 3.62694 4.00004 3.16671C4.00004 2.70647 3.62694 2.33337 3.16671 2.33337C2.70647 2.33337 2.33337 2.70647 2.33337 3.16671C2.33337 3.62694 2.70647 4.00004 3.16671 4.00004Z" fill="rgb(255,255,255)" fill-rule="nonzero" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
13
frontend/apps/allin-ssl/public/static/icons/theme-icon.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16.000000" height="16.000000" fill="none">
|
||||
<rect id="太阳1 1" width="16.000000" height="16.000000" x="0.000000" y="0.000000" />
|
||||
<path id="矢量 668" d="M7.99996 12.3333C10.3932 12.3333 12.3333 10.3932 12.3333 7.99999C12.3333 5.60676 10.3932 3.66666 7.99996 3.66666C5.60673 3.66666 3.66663 5.60676 3.66663 7.99999C3.66663 10.3932 5.60673 12.3333 7.99996 12.3333Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 668" d="M12.3333 7.99999C12.3333 5.60676 10.3932 3.66666 7.99996 3.66666C5.60673 3.66666 3.66663 5.60676 3.66663 7.99999C3.66663 10.3932 5.60673 12.3333 7.99996 12.3333C10.3932 12.3333 12.3333 10.3932 12.3333 7.99999Z" fill-rule="nonzero" stroke="rgb(0,0,0)" stroke-linejoin="round" stroke-width="1.5" />
|
||||
<path id="矢量 669" d="M7.99996 2.00001C8.46019 2.00001 8.83329 1.62691 8.83329 1.16668C8.83329 0.70644 8.46019 0.333344 7.99996 0.333344C7.53973 0.333344 7.16663 0.70644 7.16663 1.16668C7.16663 1.62691 7.53973 2.00001 7.99996 2.00001Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 670" d="M12.8333 4.00001C13.2936 4.00001 13.6667 3.62691 13.6667 3.16668C13.6667 2.70644 13.2936 2.33334 12.8333 2.33334C12.3731 2.33334 12 2.70644 12 3.16668C12 3.62691 12.3731 4.00001 12.8333 4.00001Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 671" d="M14.8333 8.83332C15.2936 8.83332 15.6667 8.46022 15.6667 7.99999C15.6667 7.53976 15.2936 7.16666 14.8333 7.16666C14.3731 7.16666 14 7.53976 14 7.99999C14 8.46022 14.3731 8.83332 14.8333 8.83332Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 672" d="M12.8333 13.6667C13.2936 13.6667 13.6667 13.2936 13.6667 12.8333C13.6667 12.3731 13.2936 12 12.8333 12C12.3731 12 12 12.3731 12 12.8333C12 13.2936 12.3731 13.6667 12.8333 13.6667Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 673" d="M7.99996 15.6667C8.46019 15.6667 8.83329 15.2936 8.83329 14.8333C8.83329 14.3731 8.46019 14 7.99996 14C7.53973 14 7.16663 14.3731 7.16663 14.8333C7.16663 15.2936 7.53973 15.6667 7.99996 15.6667Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 674" d="M3.16671 13.6667C3.62694 13.6667 4.00004 13.2936 4.00004 12.8333C4.00004 12.3731 3.62694 12 3.16671 12C2.70647 12 2.33337 12.3731 2.33337 12.8333C2.33337 13.2936 2.70647 13.6667 3.16671 13.6667Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 675" d="M1.16671 8.83332C1.62694 8.83332 2.00004 8.46022 2.00004 7.99999C2.00004 7.53976 1.62694 7.16666 1.16671 7.16666C0.706471 7.16666 0.333374 7.53976 0.333374 7.99999C0.333374 8.46022 0.706471 8.83332 1.16671 8.83332Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 676" d="M3.16671 4.00001C3.62694 4.00001 4.00004 3.62691 4.00004 3.16668C4.00004 2.70644 3.62694 2.33334 3.16671 2.33334C2.70647 2.33334 2.33337 2.70644 2.33337 3.16668C2.33337 3.62691 2.70647 4.00001 3.16671 4.00001Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
BIN
frontend/apps/allin-ssl/public/static/images/logo-dark.png
Normal file
|
After Width: | Height: | Size: 343 KiB |
@@ -1,13 +1,13 @@
|
||||
import { Transition, type Component as ComponentType, h } from 'vue'
|
||||
import { RouterView } from 'vue-router'
|
||||
import CustomProvider from '@baota/naive-ui/components/customProvider'
|
||||
import AllinSslThemeProvider from '@/components/AllinSslThemeProvider'
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
setup() {
|
||||
return () => (
|
||||
<CustomProvider>
|
||||
<AllinSslThemeProvider>
|
||||
<RouterView>
|
||||
{({ Component }: { Component: ComponentType }) => (
|
||||
<Transition name="route-slide" mode="out-in">
|
||||
@@ -15,7 +15,7 @@ export default defineComponent({
|
||||
</Transition>
|
||||
)}
|
||||
</RouterView>
|
||||
</CustomProvider>
|
||||
</AllinSslThemeProvider>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<svg viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="21.000000" height="21.000000" fill="none" customFrame="#000000">
|
||||
<defs>
|
||||
<linearGradient id="paint_linear_8" x1="0" x2="23.0083084" y1="-3.81469727e-06" y2="2.49735999" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="rgb(156,98,64)" offset="0" stop-opacity="1" />
|
||||
<stop stop-color="rgb(255,207,118)" offset="1" stop-opacity="1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint_linear_9" x1="0" x2="23.0083084" y1="-3.81469727e-06" y2="2.49735999" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="rgb(156,98,64)" offset="0" stop-opacity="1" />
|
||||
<stop stop-color="rgb(255,207,118)" offset="1" stop-opacity="1" />
|
||||
</linearGradient>
|
||||
<clipPath id="clipPath_2">
|
||||
<rect width="13.000000" height="13.000000" x="4.000000" y="4.000000" fill="rgb(255,255,255)" />
|
||||
</clipPath>
|
||||
<clipPath id="clipPath_3">
|
||||
<rect width="13.000000" height="13.000000" x="4.000000" y="4.000000" fill="rgb(255,255,255)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="组合 54">
|
||||
<rect id="矩形 41" width="21.000000" height="21.000000" x="0.000000" y="0.000000" rx="6.000000" fill="url(#paint_linear_8)" />
|
||||
<rect id="矩形 41" width="20.000000" height="20.000000" x="0.500000" y="0.500000" rx="5.500000" stroke="url(#paint_linear_9)" stroke-width="1" />
|
||||
<g id="svg 10" clip-path="url(#clipPath_2)" customFrame="url(#clipPath_2)">
|
||||
<rect id="svg 10" width="13.000000" height="13.000000" x="4.000000" y="4.000000" />
|
||||
<g id="svg 10" clip-path="url(#clipPath_3)" customFrame="url(#clipPath_3)">
|
||||
<rect id="svg 10" width="13.000000" height="13.000000" x="4.000000" y="4.000000" />
|
||||
<path id="矢量 79" d="M10.4999 4.72217C7.32217 4.72217 4.72217 7.32217 4.72217 10.4999C4.72217 13.6777 7.32217 16.2777 10.4999 16.2777C13.6777 16.2777 16.2777 13.6777 16.2777 10.4999C16.2777 7.32217 13.6777 4.72217 10.4999 4.72217ZM10.4999 15.5555C7.68327 15.5555 5.44439 13.3166 5.44439 10.4999C5.44439 7.68327 7.68327 5.44439 10.4999 5.44439C13.3166 5.44439 15.5555 7.68327 15.5555 10.4999C15.5555 13.3166 13.3166 15.5555 10.4999 15.5555Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 79" d="M4.72217 10.4999C4.72217 13.6777 7.32217 16.2777 10.4999 16.2777C13.6777 16.2777 16.2777 13.6777 16.2777 10.4999C16.2777 7.32217 13.6777 4.72217 10.4999 4.72217C7.32217 4.72217 4.72217 7.32217 4.72217 10.4999ZM5.44439 10.4999C5.44439 7.68327 7.68327 5.44439 10.4999 5.44439C13.3166 5.44439 15.5555 7.68327 15.5555 10.4999C15.5555 13.3166 13.3166 15.5555 10.4999 15.5555C7.68327 15.5555 5.44439 13.3166 5.44439 10.4999Z" fill-rule="nonzero" stroke="rgb(0,0,0)" stroke-width="0.5" />
|
||||
<path id="矢量 80" d="M10.5001 12.6667C10.2834 12.6667 10.1389 12.7389 9.9945 12.8834C9.85006 13.0278 9.77783 13.1722 9.77783 13.3889C9.77783 13.6056 9.85006 13.75 9.9945 13.8945C10.1389 14.0389 10.2834 14.1111 10.5001 14.1111C10.7167 14.1111 10.8612 14.0389 11.0056 13.8945C11.1501 13.75 11.2223 13.6056 11.2223 13.3889C11.2223 13.1722 11.1501 13.0278 11.0056 12.8834C10.8612 12.7389 10.7167 12.6667 10.5001 12.6667L10.5001 12.6667ZM9.85005 6.88892L10.0667 12.0889L10.8612 12.0889L11.1501 6.88892L9.85005 6.88892Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 80" d="M9.9945 12.8834C9.85006 13.0278 9.77783 13.1722 9.77783 13.3889C9.77783 13.6056 9.85006 13.75 9.9945 13.8945C10.1389 14.0389 10.2834 14.1111 10.5001 14.1111C10.7167 14.1111 10.8612 14.0389 11.0056 13.8945C11.1501 13.75 11.2223 13.6056 11.2223 13.3889C11.2223 13.1722 11.1501 13.0278 11.0056 12.8834C10.8612 12.7389 10.7167 12.6667 10.5001 12.6667L10.5001 12.6667C10.2834 12.6667 10.1389 12.7389 9.9945 12.8834ZM10.0667 12.0889L10.8612 12.0889L11.1501 6.88892L9.85005 6.88892L10.0667 12.0889Z" fill-rule="nonzero" stroke="rgb(0,0,0)" stroke-width="0.300000012" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,24 @@
|
||||
<svg viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="21.000000" height="21.000000" fill="none" customFrame="#000000">
|
||||
<defs>
|
||||
<clipPath id="clipPath_2">
|
||||
<rect width="13.000000" height="13.000000" x="4.000000" y="4.000000" fill="rgb(255,255,255)" />
|
||||
</clipPath>
|
||||
<clipPath id="clipPath_3">
|
||||
<rect width="13.000000" height="13.000000" x="4.000000" y="4.000000" fill="rgb(255,255,255)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="组合 54">
|
||||
<rect id="矩形 41" width="21.000000" height="21.000000" x="0.000000" y="0.000000" rx="6.000000" fill="rgb(255,242,242)" fill-opacity="0" />
|
||||
<rect id="矩形 41" width="19.500000" height="19.500000" x="0.750000" y="0.750000" rx="5.250000" stroke="rgb(231,78,78)" stroke-width="1.5" />
|
||||
<g id="svg 10" clip-path="url(#clipPath_2)" customFrame="url(#clipPath_2)">
|
||||
<rect id="svg 10" width="13.000000" height="13.000000" x="4.000000" y="4.000000" />
|
||||
<g id="svg 10" clip-path="url(#clipPath_3)" customFrame="url(#clipPath_3)">
|
||||
<rect id="svg 10" width="13.000000" height="13.000000" x="4.000000" y="4.000000" />
|
||||
<path id="矢量 79" d="M10.4999 4.72223C7.32217 4.72223 4.72217 7.32223 4.72217 10.5C4.72217 13.6778 7.32217 16.2778 10.4999 16.2778C13.6777 16.2778 16.2777 13.6778 16.2777 10.5C16.2777 7.32223 13.6777 4.72223 10.4999 4.72223ZM10.4999 15.5556C7.68327 15.5556 5.44439 13.3167 5.44439 10.5C5.44439 7.68333 7.68327 5.44445 10.4999 5.44445C13.3166 5.44445 15.5555 7.68333 15.5555 10.5C15.5555 13.3167 13.3166 15.5556 10.4999 15.5556Z" fill="rgb(255,136,136)" fill-rule="nonzero" />
|
||||
<path id="矢量 79" d="M4.72217 10.5C4.72217 13.6778 7.32217 16.2778 10.4999 16.2778C13.6777 16.2778 16.2777 13.6778 16.2777 10.5C16.2777 7.32223 13.6777 4.72223 10.4999 4.72223C7.32217 4.72223 4.72217 7.32223 4.72217 10.5ZM5.44439 10.5C5.44439 7.68333 7.68327 5.44445 10.4999 5.44445C13.3166 5.44445 15.5555 7.68333 15.5555 10.5C15.5555 13.3167 13.3166 15.5556 10.4999 15.5556C7.68327 15.5556 5.44439 13.3167 5.44439 10.5Z" fill-rule="nonzero" stroke="rgb(255,136,136)" stroke-width="0.5" />
|
||||
<path id="矢量 80" d="M10.5001 12.6667C10.2834 12.6667 10.1389 12.7389 9.9945 12.8833C9.85006 13.0278 9.77783 13.1722 9.77783 13.3889C9.77783 13.6055 9.85006 13.75 9.9945 13.8944C10.1389 14.0389 10.2834 14.1111 10.5001 14.1111C10.7167 14.1111 10.8612 14.0389 11.0056 13.8944C11.1501 13.75 11.2223 13.6055 11.2223 13.3889C11.2223 13.1722 11.1501 13.0278 11.0056 12.8833C10.8612 12.7389 10.7167 12.6667 10.5001 12.6667L10.5001 12.6667ZM9.85005 6.88889L10.0667 12.0889L10.8612 12.0889L11.1501 6.88889L9.85005 6.88889Z" fill="rgb(255,136,136)" fill-rule="nonzero" />
|
||||
<path id="矢量 80" d="M9.9945 12.8833C9.85006 13.0278 9.77783 13.1722 9.77783 13.3889C9.77783 13.6055 9.85006 13.75 9.9945 13.8944C10.1389 14.0389 10.2834 14.1111 10.5001 14.1111C10.7167 14.1111 10.8612 14.0389 11.0056 13.8944C11.1501 13.75 11.2223 13.6055 11.2223 13.3889C11.2223 13.1722 11.1501 13.0278 11.0056 12.8833C10.8612 12.7389 10.7167 12.6667 10.5001 12.6667L10.5001 12.6667C10.2834 12.6667 10.1389 12.7389 9.9945 12.8833ZM10.0667 12.0889L10.8612 12.0889L11.1501 6.88889L9.85005 6.88889L10.0667 12.0889Z" fill-rule="nonzero" stroke="rgb(255,136,136)" stroke-width="0.300000012" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,26 @@
|
||||
<svg viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="21.000000" height="21.000000" fill="none" customFrame="#000000">
|
||||
<defs>
|
||||
<linearGradient id="paint_linear_4" x1="0" x2="23.0083084" y1="-3.81469727e-06" y2="2.49735999" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="rgb(156,98,64)" offset="0" stop-opacity="1" />
|
||||
<stop stop-color="rgb(255,207,118)" offset="1" stop-opacity="1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint_linear_5" x1="0" x2="23.0083084" y1="-3.81469727e-06" y2="2.49735999" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="rgb(156,98,64)" offset="0" stop-opacity="1" />
|
||||
<stop stop-color="rgb(255,207,118)" offset="1" stop-opacity="1" />
|
||||
</linearGradient>
|
||||
<clipPath id="clipPath_1">
|
||||
<rect width="13.000000" height="13.000000" x="4.000000" y="4.000000" fill="rgb(255,255,255)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="组合 53">
|
||||
<rect id="矩形 40" width="21.000000" height="21.000000" x="0.000000" y="0.000000" rx="6.000000" fill="url(#paint_linear_4)" />
|
||||
<rect id="矩形 40" width="20.000000" height="20.000000" x="0.500000" y="0.500000" rx="5.500000" stroke="url(#paint_linear_5)" stroke-width="1" />
|
||||
<g id="svg 9" clip-path="url(#clipPath_1)" customFrame="url(#clipPath_1)">
|
||||
<rect id="svg 9" width="13.000000" height="13.000000" x="4.000000" y="4.000000" />
|
||||
<path id="矢量 77" d="M15.7724 8.57917C15.5147 7.9698 15.1452 7.42136 14.6742 6.95164C14.2045 6.48191 13.6561 6.11248 13.0467 5.85349C12.4145 5.58689 11.7442 5.45105 11.0535 5.45105C10.3629 5.45105 9.69133 5.58689 9.05911 5.85349C8.44973 6.11121 7.90129 6.48064 7.43157 6.95164C6.96184 7.42136 6.59241 7.9698 6.33342 8.57917C6.06682 9.2114 5.93098 9.88171 5.93098 10.5723C5.93098 10.7653 5.94241 10.9595 5.96399 11.1512L5.2175 10.4378C5.11594 10.3413 4.95598 10.3438 4.85823 10.4454C4.76174 10.5469 4.76428 10.7069 4.86584 10.8047L6.17346 12.0552C6.22043 12.1009 6.28391 12.125 6.34866 12.125L6.35881 12.125C6.42737 12.1224 6.49211 12.0907 6.53782 12.0399L7.73752 10.6828C7.8302 10.5774 7.82004 10.4175 7.71594 10.3248C7.61057 10.2321 7.45061 10.2423 7.35793 10.3464L6.49719 11.3188C6.45784 11.0738 6.43752 10.8224 6.43752 10.5723C6.43752 8.0282 8.50686 5.95886 11.051 5.95886C13.5951 5.95886 15.6645 8.0282 15.6645 10.5723C15.6645 13.1165 13.5951 15.1858 11.051 15.1858C9.87795 15.1858 8.75949 14.7453 7.90256 13.9442C7.79973 13.849 7.63977 13.8541 7.54328 13.9569C7.4468 14.0597 7.45315 14.2197 7.55598 14.3162C8.50686 15.2049 9.74846 15.6949 11.051 15.6949C11.7429 15.6949 12.4132 15.5591 13.0442 15.2925C13.6535 15.0347 14.202 14.6653 14.6717 14.1943C15.1414 13.7246 15.5109 13.1761 15.7698 12.5668C16.0364 11.9345 16.1723 11.2642 16.1723 10.5736C16.1748 9.88171 16.039 9.21013 15.7724 8.57917L15.7724 8.57917Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 77" d="M14.6742 6.95164C14.2045 6.48191 13.6561 6.11248 13.0467 5.85349C12.4145 5.58689 11.7442 5.45105 11.0535 5.45105C10.3629 5.45105 9.69133 5.58689 9.05911 5.85349C8.44973 6.11121 7.90129 6.48064 7.43157 6.95164C6.96184 7.42136 6.59241 7.9698 6.33342 8.57917C6.06682 9.2114 5.93098 9.88171 5.93098 10.5723C5.93098 10.7653 5.94241 10.9595 5.96399 11.1512L5.2175 10.4378C5.11594 10.3413 4.95598 10.3438 4.85823 10.4454C4.76174 10.5469 4.76428 10.7069 4.86584 10.8047L6.17346 12.0552C6.22043 12.1009 6.28391 12.125 6.34866 12.125L6.35881 12.125C6.42737 12.1224 6.49211 12.0907 6.53782 12.0399L7.73752 10.6828C7.8302 10.5774 7.82004 10.4175 7.71594 10.3248C7.61057 10.2321 7.45061 10.2423 7.35793 10.3464L6.49719 11.3188C6.45784 11.0738 6.43752 10.8224 6.43752 10.5723C6.43752 8.0282 8.50686 5.95886 11.051 5.95886C13.5951 5.95886 15.6645 8.0282 15.6645 10.5723C15.6645 13.1165 13.5951 15.1858 11.051 15.1858C9.87795 15.1858 8.75949 14.7453 7.90256 13.9442C7.79973 13.849 7.63977 13.8541 7.54328 13.9569C7.4468 14.0597 7.45315 14.2197 7.55598 14.3162C8.50686 15.2049 9.74846 15.6949 11.051 15.6949C11.7429 15.6949 12.4132 15.5591 13.0442 15.2925C13.6535 15.0347 14.202 14.6653 14.6717 14.1943C15.1414 13.7246 15.5109 13.1761 15.7698 12.5668C16.0364 11.9345 16.1723 11.2642 16.1723 10.5736C16.1748 9.88171 16.039 9.21013 15.7724 8.57917L15.7724 8.57917C15.5147 7.9698 15.1452 7.42136 14.6742 6.95164Z" fill-rule="nonzero" stroke="rgb(0,0,0)" stroke-width="0.800000012" />
|
||||
<path id="矢量 78" d="M10.6777 7.49756C10.5089 7.49756 10.373 7.6334 10.373 7.80225L10.373 10.9583C10.373 11.1271 10.5089 11.263 10.6777 11.263L13.8338 11.263C14.0026 11.263 14.1385 11.1271 14.1385 10.9583C14.1385 10.7895 14.0026 10.6536 13.8338 10.6536L10.9824 10.6536L10.9824 7.80225C10.9824 7.63467 10.8466 7.49756 10.6777 7.49756Z" fill="rgb(0,0,0)" fill-rule="nonzero" />
|
||||
<path id="矢量 78" d="M10.373 7.80225L10.373 10.9583C10.373 11.1271 10.5089 11.263 10.6777 11.263L13.8338 11.263C14.0026 11.263 14.1385 11.1271 14.1385 10.9583C14.1385 10.7895 14.0026 10.6536 13.8338 10.6536L10.9824 10.6536L10.9824 7.80225C10.9824 7.63467 10.8466 7.49756 10.6777 7.49756C10.5089 7.49756 10.373 7.6334 10.373 7.80225Z" fill-rule="nonzero" stroke="rgb(0,0,0)" stroke-width="0.800000012" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,18 @@
|
||||
<svg viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="21.000000" height="21.000000" fill="none" customFrame="#000000">
|
||||
<defs>
|
||||
<clipPath id="clipPath_1">
|
||||
<rect width="13.000000" height="13.000000" x="4.000000" y="4.000000" fill="rgb(255,255,255)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="组合 53">
|
||||
<rect id="矩形 40" width="21.000000" height="21.000000" x="0.000000" y="0.000000" rx="6.000000" fill="rgb(243,255,241)" fill-opacity="0" />
|
||||
<rect id="矩形 40" width="19.500000" height="19.500000" x="0.750000" y="0.750000" rx="5.250000" stroke="rgb(255,118,24)" stroke-width="1.5" />
|
||||
<g id="svg 9" clip-path="url(#clipPath_1)" customFrame="url(#clipPath_1)">
|
||||
<rect id="svg 9" width="13.000000" height="13.000000" x="4.000000" y="4.000000" />
|
||||
<path id="矢量 77" d="M15.7724 8.57921C15.5147 7.96983 15.1452 7.42139 14.6742 6.95167C14.2045 6.48194 13.6561 6.11251 13.0467 5.85352C12.4145 5.58692 11.7442 5.45108 11.0535 5.45108C10.3629 5.45108 9.69133 5.58692 9.05911 5.85352C8.44973 6.11124 7.90129 6.48067 7.43157 6.95167C6.96184 7.42139 6.59241 7.96983 6.33342 8.57921C6.06682 9.21143 5.93098 9.88174 5.93098 10.5724C5.93098 10.7653 5.94241 10.9596 5.96399 11.1513L5.2175 10.4378C5.11594 10.3413 4.95598 10.3439 4.85823 10.4454C4.76174 10.547 4.76428 10.7069 4.86584 10.8047L6.17346 12.0552C6.22043 12.1009 6.28391 12.125 6.34866 12.125L6.35881 12.125C6.42737 12.1225 6.49211 12.0907 6.53782 12.0399L7.73752 10.6828C7.8302 10.5774 7.82004 10.4175 7.71594 10.3248C7.61057 10.2321 7.45061 10.2423 7.35793 10.3464L6.49719 11.3189C6.45784 11.0738 6.43752 10.8225 6.43752 10.5724C6.43752 8.02823 8.50686 5.95889 11.051 5.95889C13.5951 5.95889 15.6645 8.02823 15.6645 10.5724C15.6645 13.1165 13.5951 15.1858 11.051 15.1858C9.87795 15.1858 8.75949 14.7453 7.90256 13.9442C7.79973 13.849 7.63977 13.8541 7.54328 13.9569C7.4468 14.0598 7.45315 14.2197 7.55598 14.3162C8.50686 15.2049 9.74846 15.6949 11.051 15.6949C11.7429 15.6949 12.4132 15.5591 13.0442 15.2925C13.6535 15.0348 14.202 14.6653 14.6717 14.1943C15.1414 13.7246 15.5109 13.1762 15.7698 12.5668C16.0364 11.9346 16.1723 11.2643 16.1723 10.5736C16.1748 9.88174 16.039 9.21016 15.7724 8.5792L15.7724 8.57921Z" fill="rgb(255,163,84)" fill-rule="nonzero" />
|
||||
<path id="矢量 77" d="M14.6742 6.95167C14.2045 6.48194 13.6561 6.11251 13.0467 5.85352C12.4145 5.58692 11.7442 5.45108 11.0535 5.45108C10.3629 5.45108 9.69133 5.58692 9.05911 5.85352C8.44973 6.11124 7.90129 6.48067 7.43157 6.95167C6.96184 7.42139 6.59241 7.96983 6.33342 8.57921C6.06682 9.21143 5.93098 9.88174 5.93098 10.5724C5.93098 10.7653 5.94241 10.9596 5.96399 11.1513L5.2175 10.4378C5.11594 10.3413 4.95598 10.3439 4.85823 10.4454C4.76174 10.547 4.76428 10.7069 4.86584 10.8047L6.17346 12.0552C6.22043 12.1009 6.28391 12.125 6.34866 12.125L6.35881 12.125C6.42737 12.1225 6.49211 12.0907 6.53782 12.0399L7.73752 10.6828C7.8302 10.5774 7.82004 10.4175 7.71594 10.3248C7.61057 10.2321 7.45061 10.2423 7.35793 10.3464L6.49719 11.3189C6.45784 11.0738 6.43752 10.8225 6.43752 10.5724C6.43752 8.02823 8.50686 5.95889 11.051 5.95889C13.5951 5.95889 15.6645 8.02823 15.6645 10.5724C15.6645 13.1165 13.5951 15.1858 11.051 15.1858C9.87795 15.1858 8.75949 14.7453 7.90256 13.9442C7.79973 13.849 7.63977 13.8541 7.54328 13.9569C7.4468 14.0598 7.45315 14.2197 7.55598 14.3162C8.50686 15.2049 9.74846 15.6949 11.051 15.6949C11.7429 15.6949 12.4132 15.5591 13.0442 15.2925C13.6535 15.0348 14.202 14.6653 14.6717 14.1943C15.1414 13.7246 15.5109 13.1762 15.7698 12.5668C16.0364 11.9346 16.1723 11.2643 16.1723 10.5736C16.1748 9.88174 16.039 9.21016 15.7724 8.5792L15.7724 8.57921C15.5147 7.96983 15.1452 7.42139 14.6742 6.95167Z" fill-rule="nonzero" stroke="rgb(255,163,84)" stroke-width="0.800000012" />
|
||||
<path id="矢量 78" d="M10.6777 7.49756C10.5089 7.49756 10.373 7.6334 10.373 7.80225L10.373 10.9583C10.373 11.1271 10.5089 11.263 10.6777 11.263L13.8338 11.263C14.0026 11.263 14.1385 11.1271 14.1385 10.9583C14.1385 10.7895 14.0026 10.6536 13.8338 10.6536L10.9824 10.6536L10.9824 7.80225C10.9824 7.63467 10.8466 7.49756 10.6777 7.49756Z" fill="rgb(255,163,84)" fill-rule="nonzero" />
|
||||
<path id="矢量 78" d="M10.373 7.80225L10.373 10.9583C10.373 11.1271 10.5089 11.263 10.6777 11.263L13.8338 11.263C14.0026 11.263 14.1385 11.1271 14.1385 10.9583C14.1385 10.7895 14.0026 10.6536 13.8338 10.6536L10.9824 10.6536L10.9824 7.80225C10.9824 7.63467 10.8466 7.49756 10.6777 7.49756C10.5089 7.49756 10.373 7.6334 10.373 7.80225Z" fill-rule="nonzero" stroke="rgb(255,163,84)" stroke-width="0.800000012" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
BIN
frontend/apps/allin-ssl/src/assets/images/home-quick-dark1.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
frontend/apps/allin-ssl/src/assets/images/home-quick-dark2.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
frontend/apps/allin-ssl/src/assets/images/home-quick-dark3.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
frontend/apps/allin-ssl/src/assets/images/home-quick-entry1.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
frontend/apps/allin-ssl/src/assets/images/home-quick-entry2.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
frontend/apps/allin-ssl/src/assets/images/home-quick-entry3.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,61 @@
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import { NConfigProvider, NDialogProvider, NMessageProvider, NModalProvider, NNotificationProvider, zhCN, dateZhCN } from 'naive-ui'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useTheme } from '@baota/naive-ui/theme'
|
||||
import { useNaiveI18nSync } from '@baota/naive-ui/i18n'
|
||||
import { allinSslDarkThemeOverrides, allinSslLightThemeOverrides } from '@config/theme'
|
||||
|
||||
/**
|
||||
* AllinSSL 自定义主题提供者
|
||||
* 在 CustomProvider 基础上添加项目特定的主题覆盖
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'AllinSslThemeProvider',
|
||||
setup(_, { slots }) {
|
||||
const { locale } = useI18n()
|
||||
const { naiveLocale, naiveDateLocale } = useNaiveI18nSync(locale)
|
||||
const { theme, themeOverrides, isDark } = useTheme()
|
||||
|
||||
// 合并主题配置:base theme + 项目特定主题
|
||||
const mergedThemeOverrides = computed(() => {
|
||||
const baseOverrides = themeOverrides.value || {}
|
||||
const projectOverrides = isDark.value ? allinSslDarkThemeOverrides : allinSslLightThemeOverrides
|
||||
|
||||
// 深度合并配置
|
||||
return {
|
||||
...baseOverrides,
|
||||
...projectOverrides,
|
||||
// 特殊处理 common,因为它包含多个属性
|
||||
common: {
|
||||
...baseOverrides.common,
|
||||
...projectOverrides.common,
|
||||
},
|
||||
Card: {
|
||||
...baseOverrides.Card,
|
||||
...projectOverrides.Card,
|
||||
},
|
||||
Layout: {
|
||||
...baseOverrides.Layout,
|
||||
...projectOverrides.Layout,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
return () => (
|
||||
<NConfigProvider
|
||||
theme={theme.value}
|
||||
theme-overrides={mergedThemeOverrides.value}
|
||||
locale={naiveLocale.value || zhCN}
|
||||
date-locale={naiveDateLocale.value || dateZhCN}
|
||||
>
|
||||
<NDialogProvider>
|
||||
<NMessageProvider>
|
||||
<NNotificationProvider>
|
||||
<NModalProvider>{slots.default?.()}</NModalProvider>
|
||||
</NNotificationProvider>
|
||||
</NMessageProvider>
|
||||
</NDialogProvider>
|
||||
</NConfigProvider>
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -70,7 +70,7 @@ export default defineComponent({
|
||||
header: () => props.title,
|
||||
'header-extra': () => (
|
||||
<NSpace>
|
||||
<NButton onClick={refreshLogs} size="small" type="primary" disabled={isLoading.value}>
|
||||
<NButton onClick={refreshLogs} class="gradient-primary-btn" size="small" type="primary" disabled={isLoading.value}>
|
||||
{{
|
||||
icon: () => (
|
||||
<NIcon>
|
||||
@@ -81,7 +81,7 @@ export default defineComponent({
|
||||
}}
|
||||
</NButton>
|
||||
{props.enableDownload && (
|
||||
<NButton onClick={downloadLogs} size="small" disabled={isLoading.value || !logContent.value.length}>
|
||||
<NButton class="gradient-default-btn" onClick={downloadLogs} size="small" disabled={isLoading.value || !logContent.value.length}>
|
||||
{{
|
||||
icon: () => (
|
||||
<NIcon>
|
||||
|
||||
@@ -109,12 +109,12 @@ export default defineComponent<DnsProviderSelectProps>({
|
||||
<NSpin show={controller.isLoading.value}>
|
||||
<NGrid cols={24} class={props.customClass}>
|
||||
<NFormItemGi
|
||||
span={props.isAddMode ? 13 : 24}
|
||||
span={props.isAddMode ? 14 : 24}
|
||||
label={props.type === 'dns' ? $t('t_3_1745735765112') : $t('t_0_1746754500246')}
|
||||
path={props.path}
|
||||
>
|
||||
<NSelect
|
||||
class="flex-1 w-full"
|
||||
class="flex-1 w-full "
|
||||
filterable
|
||||
options={controller.dnsProviderRef.value}
|
||||
renderLabel={renderLabel}
|
||||
@@ -139,11 +139,12 @@ export default defineComponent<DnsProviderSelectProps>({
|
||||
/>
|
||||
</NFormItemGi>
|
||||
{props.isAddMode && (
|
||||
<NFormItemGi span={11}>
|
||||
<NButton class="mx-[8px]" onClick={controller.goToAddDnsProvider} disabled={props.disabled}>
|
||||
<NFormItemGi span={10}>
|
||||
<NButton class="gradient-default-btn mx-[8px]" onClick={controller.goToAddDnsProvider} disabled={props.disabled}>
|
||||
{props.type === 'dns' ? $t('t_1_1746004861166') : $t('t_3_1746858920060')}
|
||||
</NButton>
|
||||
<NButton
|
||||
class="gradient-default-btn"
|
||||
onClick={() => controller.loadDnsProviders(props.type)}
|
||||
loading={controller.isLoading.value}
|
||||
disabled={props.disabled}
|
||||
|
||||
@@ -179,4 +179,112 @@
|
||||
|
||||
.nodeMoveIcon {
|
||||
@apply w-[3.5rem] h-[3.5rem] cursor-pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.endNode {
|
||||
@apply w-[1.5rem] h-[1.5rem] rounded-[1rem] bg-[#cacaca]
|
||||
}
|
||||
|
||||
.endNodeTxt {
|
||||
@apply text-[#5a5e66] mb-[10rem] pt-2
|
||||
}
|
||||
|
||||
:global(.defaultDark) {
|
||||
.nodeBody {
|
||||
background-color: #171717;
|
||||
:global(.n-tag) {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
z-index: 0;
|
||||
background-color: #282218;
|
||||
border-radius: 30px;
|
||||
:global(.n-tag__border) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* 渐变边框 */
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
padding: 1px;
|
||||
background: var(--menu-active-gradient);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
:global(.n-tag__content) {
|
||||
background: var(--menu-active-gradient) !important;
|
||||
-webkit-background-clip: text !important;
|
||||
background-clip: text !important;
|
||||
-webkit-text-fill-color: transparent !important;
|
||||
color: transparent !important;
|
||||
|
||||
span,
|
||||
* {
|
||||
background: var(--menu-active-gradient) !important;
|
||||
-webkit-background-clip: text !important;
|
||||
background-clip: text !important;
|
||||
-webkit-text-fill-color: transparent !important;
|
||||
color: transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.nodeHeader {
|
||||
font-weight: 600;
|
||||
background-color: transparent!important;
|
||||
input{
|
||||
color: #000!important;
|
||||
}
|
||||
}
|
||||
.nodeContent {
|
||||
border-radius: 6px;
|
||||
padding: 1px;
|
||||
background-color: transparent;
|
||||
background: var(--menu-active-gradient);
|
||||
}
|
||||
.nodeArrows::before {
|
||||
display: none;
|
||||
}
|
||||
.endNode {
|
||||
background-image: linear-gradient(45deg, #FFF4DA 0%, #FFCD72 100%)!important;
|
||||
}
|
||||
.endNodeTxt {
|
||||
background-image: linear-gradient(45deg, #FFF4DA 0%, #FFCD72 100%);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
color: transparent;
|
||||
}
|
||||
.nodeConditionHeader {
|
||||
background-color: transparent!important;
|
||||
}
|
||||
.nodeConditionHeader input:focus {
|
||||
background-color: transparent!important;
|
||||
}
|
||||
|
||||
/* 执行结果分支 - 成功状态的边框 */
|
||||
[data-branch-type="success"] .nodeExecuteResultBranch {
|
||||
border: 1px solid var(--n-success-status-color) !important;
|
||||
input {
|
||||
color: var(--n-success-status-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 执行结果分支 - 失败状态的边框 */
|
||||
[data-branch-type="fail"] .nodeExecuteResultBranch {
|
||||
border: 1px solid var(--n-error-primary-color) !important;
|
||||
input {
|
||||
color: var(--n-error-primary-color) !important;
|
||||
}
|
||||
}
|
||||
.nodeExecuteResultBranch {
|
||||
background-image: none!important;
|
||||
background-color: #161616!important;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { markRaw } from 'vue'
|
||||
import { useStore } from '@components/FlowChart/useStore'
|
||||
import nodeOptions from '@components/FlowChart/lib/config'
|
||||
import { useDialog } from '@baota/naive-ui/hooks'
|
||||
import { useTheme } from "@baota/naive-ui/theme";
|
||||
import { $t } from '@locales/index'
|
||||
import { CONDITION, EXECUTE_RESULT_CONDITION, START } from '@components/FlowChart/lib/alias'
|
||||
import { useNodeValidator } from '@components/FlowChart/lib/verify'
|
||||
@@ -16,233 +17,304 @@ import styles from './index.module.css'
|
||||
import ErrorNode from '../errorNode/index'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BaseNode',
|
||||
props: {
|
||||
// 节点数据
|
||||
node: {
|
||||
type: Object as PropType<BaseNodeData>,
|
||||
required: true, // 自读
|
||||
},
|
||||
},
|
||||
setup(props: BaseNodeProps) {
|
||||
// 注入任务节点组件映射
|
||||
const taskComponents = inject('taskComponents', {}) as Record<string, Component>
|
||||
name: "BaseNode",
|
||||
props: {
|
||||
// 节点数据
|
||||
node: {
|
||||
type: Object as PropType<BaseNodeData>,
|
||||
required: true, // 自读
|
||||
},
|
||||
},
|
||||
setup(props: BaseNodeProps) {
|
||||
// 注入任务节点组件映射
|
||||
const taskComponents = inject("taskComponents", {}) as Record<
|
||||
string,
|
||||
Component
|
||||
>;
|
||||
|
||||
// ====================== 基础状态数据 ======================
|
||||
const { validator, validate } = useNodeValidator() // 验证器
|
||||
const tempNodeId = ref(props.node.id || uuidv4()) // 节点id
|
||||
const config = ref<BaseRenderNodeOptions<BaseNodeData>>(nodeOptions[props.node.type]() || {}) // 节点配置
|
||||
const nodeNameRef = ref<HTMLInputElement | null>(null) // 节点名称输入框
|
||||
const isShowEditNodeName = ref(false) // 是否显示编辑节点名称
|
||||
const inputValue = ref(props.node.name) // 输入框值
|
||||
const renderNodeContent = ref() // 节点组件
|
||||
const nodeContentRef = ref() // 节点内容
|
||||
const { removeNode, updateNode, selectedNodeId, selectedNode } = useStore()
|
||||
// ====================== 基础状态数据 ======================
|
||||
const { validator, validate } = useNodeValidator(); // 验证器
|
||||
const tempNodeId = ref(props.node.id || uuidv4()); // 节点id
|
||||
const config = ref<BaseRenderNodeOptions<BaseNodeData>>(
|
||||
nodeOptions[props.node.type]() || {}
|
||||
); // 节点配置
|
||||
const nodeNameRef = ref<HTMLInputElement | null>(null); // 节点名称输入框
|
||||
const isShowEditNodeName = ref(false); // 是否显示编辑节点名称
|
||||
const inputValue = ref(props.node.name); // 输入框值
|
||||
const renderNodeContent = ref(); // 节点组件
|
||||
const nodeContentRef = ref(); // 节点内容
|
||||
const { removeNode, updateNode, selectedNodeId, selectedNode } = useStore();
|
||||
|
||||
// ====================== 节点状态数据 ======================
|
||||
// 错误状态
|
||||
const errorState = ref({
|
||||
isError: false,
|
||||
message: null as string | null,
|
||||
showTips: false,
|
||||
})
|
||||
// ====================== 节点状态数据 ======================
|
||||
// 错误状态
|
||||
const errorState = ref({
|
||||
isError: false,
|
||||
message: null as string | null,
|
||||
showTips: false,
|
||||
});
|
||||
|
||||
// ====================== 计算属性 ======================
|
||||
// 是否是开始节点
|
||||
const isStart = computed(() => props.node.type === START)
|
||||
// ====================== 计算属性 ======================
|
||||
// 是否是开始节点
|
||||
const isStart = computed(() => props.node.type === START);
|
||||
|
||||
// 是否可以删除
|
||||
const isRemoved = computed(() => config.value?.operateNode?.remove)
|
||||
// 是否可以删除
|
||||
const isRemoved = computed(() => config.value?.operateNode?.remove);
|
||||
|
||||
// 是否是条件节点
|
||||
const isCondition = computed(() => [CONDITION, EXECUTE_RESULT_CONDITION].includes(props.node.type))
|
||||
// 是否是条件节点
|
||||
const isCondition = computed(() =>
|
||||
[CONDITION, EXECUTE_RESULT_CONDITION].includes(props.node.type)
|
||||
);
|
||||
|
||||
// 根据节点类型获取图标
|
||||
const typeIcon: ComputedRef<string> = computed(() => {
|
||||
const type = {
|
||||
success: 'flow-success',
|
||||
fail: 'flow-error',
|
||||
}
|
||||
// console.log(props.node.config?.type)
|
||||
if (props.node.type === EXECUTE_RESULT_CONDITION)
|
||||
return (type[props.node.config?.type as keyof typeof type] || '') as string
|
||||
return ''
|
||||
})
|
||||
// 是否是执行结果分支节点(执行结果条件节点)
|
||||
const isExecuteResultBranch = computed(
|
||||
() => props.node.type === EXECUTE_RESULT_CONDITION
|
||||
);
|
||||
|
||||
// 根据节点类型获取图标颜色
|
||||
const typeIconColor: ComputedRef<string> = computed(() => {
|
||||
if (props.node.type === EXECUTE_RESULT_CONDITION) return (props.node.config?.type || '') as string
|
||||
return '#FFFFFF'
|
||||
})
|
||||
const { isDark } = useTheme();
|
||||
// 根据节点类型获取图标
|
||||
const typeIcon: ComputedRef<string> = computed(() => {
|
||||
const type = {
|
||||
success: "flow-success",
|
||||
fail: "flow-error",
|
||||
};
|
||||
// console.log(props.node.config?.type)
|
||||
if (props.node.type === EXECUTE_RESULT_CONDITION)
|
||||
return (type[props.node.config?.type as keyof typeof type] ||
|
||||
"") as string;
|
||||
return "";
|
||||
});
|
||||
|
||||
// ====================== 数据监听与副作用 ======================
|
||||
// 监听节点数据,更新节点配置
|
||||
watch(
|
||||
() => props.node,
|
||||
() => {
|
||||
config.value = nodeOptions[props.node.type as NodeNum]() // 更新节点配置
|
||||
inputValue.value = props.node.name // 更新节点名称
|
||||
tempNodeId.value = props.node.id || uuidv4() // 更新节点id
|
||||
validator.validateAll() // 验证器验证
|
||||
// 根据节点类型获取图标颜色
|
||||
const typeIconColor: ComputedRef<string> = computed(() => {
|
||||
if (props.node.type === EXECUTE_RESULT_CONDITION) {
|
||||
const type = props.node.config?.type as "success" | "fail" | undefined;
|
||||
if (type === "success") {
|
||||
return "var(--n-success-status-color)";
|
||||
}
|
||||
if (type === "fail") {
|
||||
return "var(--n-error-primary-color)";
|
||||
}
|
||||
}
|
||||
return isDark.value ? "#000000" : "#FFFFFF";
|
||||
});
|
||||
|
||||
// 使用注入的taskComponents而不是props传递
|
||||
const nodeType = props.node.type
|
||||
const nodeComponentKey = `${nodeType}Node`
|
||||
// 如果taskComponents中有对应的组件就使用,否则使用ErrorNode
|
||||
if (taskComponents && taskComponents[nodeComponentKey]) {
|
||||
renderNodeContent.value = markRaw(taskComponents[nodeComponentKey])
|
||||
} else {
|
||||
renderNodeContent.value = markRaw(
|
||||
defineAsyncComponent({
|
||||
loader: () => import('@components/FlowChart/components/base/errorNode'),
|
||||
loadingComponent: () => <div>Loading...</div>,
|
||||
errorComponent: () => <ErrorNode />,
|
||||
}),
|
||||
)
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
// 根据节点类型获取关闭图标颜色
|
||||
const closeIconColor: ComputedRef<string> = computed(() => {
|
||||
if (props.node.type === EXECUTE_RESULT_CONDITION && isDark.value) {
|
||||
const type = props.node.config?.type as "success" | "fail" | undefined;
|
||||
if (type === "success") {
|
||||
return "var(--n-success-status-color)";
|
||||
}
|
||||
if (type === "fail") {
|
||||
return "var(--n-error-primary-color)";
|
||||
}
|
||||
}
|
||||
return isDark.value ? "#000" : isCondition.value ? "#333" : "#FFFFFF";
|
||||
});
|
||||
|
||||
// ====================== 节点操作方法 ======================
|
||||
/**
|
||||
* @description 显示错误提示
|
||||
* @param {boolean} flag - 是否显示
|
||||
*/
|
||||
const showErrorTips = (flag: boolean) => {
|
||||
errorState.value.showTips = flag
|
||||
}
|
||||
// ====================== 数据监听与副作用 ======================
|
||||
// 监听节点数据,更新节点配置
|
||||
watch(
|
||||
() => props.node,
|
||||
() => {
|
||||
config.value = nodeOptions[props.node.type as NodeNum](); // 更新节点配置
|
||||
inputValue.value = props.node.name; // 更新节点名称
|
||||
tempNodeId.value = props.node.id || uuidv4(); // 更新节点id
|
||||
validator.validateAll(); // 验证器验证
|
||||
|
||||
/**
|
||||
* @description 删除选中节点
|
||||
* @param {MouseEvent} ev - 事件对象
|
||||
* @param {string} id - 节点id
|
||||
* @param {BaseNodeData} node - 节点数据
|
||||
*/
|
||||
const removeFindNode = (ev: MouseEvent, id: string, node: BaseNodeData) => {
|
||||
const validator = validate(id)
|
||||
if (validator.valid) {
|
||||
useDialog({
|
||||
type: 'warning',
|
||||
title: $t('t_1_1745765875247', { name: node.name }),
|
||||
content: node.type === CONDITION ? $t('t_2_1745765875918') : $t('t_3_1745765920953'),
|
||||
onPositiveClick: () => removeNode(id),
|
||||
})
|
||||
}
|
||||
// 如果节点类型是条件节点或验证不通过,则删除节点
|
||||
if ([EXECUTE_RESULT_CONDITION].includes(node.type) || !validator.valid) {
|
||||
removeNode(id)
|
||||
}
|
||||
ev.stopPropagation()
|
||||
ev.preventDefault()
|
||||
}
|
||||
// 使用注入的taskComponents而不是props传递
|
||||
const nodeType = props.node.type;
|
||||
const nodeComponentKey = `${nodeType}Node`;
|
||||
// 如果taskComponents中有对应的组件就使用,否则使用ErrorNode
|
||||
if (taskComponents && taskComponents[nodeComponentKey]) {
|
||||
renderNodeContent.value = markRaw(taskComponents[nodeComponentKey]);
|
||||
} else {
|
||||
renderNodeContent.value = markRaw(
|
||||
defineAsyncComponent({
|
||||
loader: () =>
|
||||
import("@components/FlowChart/components/base/errorNode"),
|
||||
loadingComponent: () => <div>Loading...</div>,
|
||||
errorComponent: () => <ErrorNode />,
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/**
|
||||
* @description 点击节点
|
||||
*/
|
||||
const handleNodeClick = () => {
|
||||
console.log('nodeContentRef', nodeContentRef.value)
|
||||
if (
|
||||
nodeContentRef.value?.handleNodeClick &&
|
||||
props.node.type !== CONDITION &&
|
||||
props.node.type !== EXECUTE_RESULT_CONDITION
|
||||
) {
|
||||
selectedNodeId.value = props.node.id || ''
|
||||
nodeContentRef.value.handleNodeClick(selectedNode)
|
||||
}
|
||||
}
|
||||
// ====================== 节点操作方法 ======================
|
||||
/**
|
||||
* @description 显示错误提示
|
||||
* @param {boolean} flag - 是否显示
|
||||
*/
|
||||
const showErrorTips = (flag: boolean) => {
|
||||
errorState.value.showTips = flag;
|
||||
};
|
||||
|
||||
// ====================== 事件处理函数 ======================
|
||||
// 回车保存
|
||||
const keyupSaveNodeName = (e: KeyboardEvent) => {
|
||||
if (e.keyCode === 13) {
|
||||
isShowEditNodeName.value = false
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @description 删除选中节点
|
||||
* @param {MouseEvent} ev - 事件对象
|
||||
* @param {string} id - 节点id
|
||||
* @param {BaseNodeData} node - 节点数据
|
||||
*/
|
||||
const removeFindNode = (ev: MouseEvent, id: string, node: BaseNodeData) => {
|
||||
const validator = validate(id);
|
||||
if (validator.valid) {
|
||||
useDialog({
|
||||
type: "warning",
|
||||
title: $t("t_1_1745765875247", { name: node.name }),
|
||||
content:
|
||||
node.type === CONDITION
|
||||
? $t("t_2_1745765875918")
|
||||
: $t("t_3_1745765920953"),
|
||||
onPositiveClick: () => removeNode(id),
|
||||
});
|
||||
}
|
||||
// 如果节点类型是条件节点或验证不通过,则删除节点
|
||||
if ([EXECUTE_RESULT_CONDITION].includes(node.type) || !validator.valid) {
|
||||
removeNode(id);
|
||||
}
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
};
|
||||
|
||||
// 保存节点名称
|
||||
const saveNodeName = (e: Event) => {
|
||||
const target = e.target as HTMLInputElement
|
||||
inputValue.value = target.value
|
||||
updateNode(tempNodeId.value, { name: inputValue.value })
|
||||
}
|
||||
/**
|
||||
* @description 点击节点
|
||||
*/
|
||||
const handleNodeClick = () => {
|
||||
console.log("nodeContentRef", nodeContentRef.value);
|
||||
if (
|
||||
nodeContentRef.value?.handleNodeClick &&
|
||||
props.node.type !== CONDITION &&
|
||||
props.node.type !== EXECUTE_RESULT_CONDITION
|
||||
) {
|
||||
selectedNodeId.value = props.node.id || "";
|
||||
nodeContentRef.value.handleNodeClick(selectedNode);
|
||||
}
|
||||
};
|
||||
|
||||
// ====================== 渲染函数 ======================
|
||||
return () => (
|
||||
<div class={[styles.node, !isStart.value && styles.nodeArrows]}>
|
||||
<div class={[styles.nodeContent, isCondition.value && styles.nodeCondition]} onClick={handleNodeClick}>
|
||||
{/* 节点头部 */}
|
||||
<div
|
||||
class={[
|
||||
styles.nodeHeader,
|
||||
isCondition.value && styles.nodeConditionHeader,
|
||||
!typeIcon.value ? styles.nodeHeaderBranch : '',
|
||||
]}
|
||||
style={{
|
||||
color: config.value?.title?.color,
|
||||
backgroundColor: config.value?.title?.bgColor,
|
||||
}}
|
||||
>
|
||||
{/* 节点图标 */}
|
||||
{typeIcon.value ? (
|
||||
<SvgIcon
|
||||
icon={typeIcon.value ? typeIcon.value : config.value?.icon?.name || ''}
|
||||
class={[styles.nodeIcon, '!absolute top-[50%] left-[1rem] -mt-[.8rem]']}
|
||||
color={typeIconColor.value}
|
||||
/>
|
||||
) : null}
|
||||
// ====================== 事件处理函数 ======================
|
||||
// 回车保存
|
||||
const keyupSaveNodeName = (e: KeyboardEvent) => {
|
||||
if (e.key === "Enter") {
|
||||
isShowEditNodeName.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
{/* 节点标题 */}
|
||||
<div class={styles.nodeHeaderTitle} title="点击编辑">
|
||||
<div class={styles.nodeHeaderTitleInput}>
|
||||
<input
|
||||
ref={nodeNameRef}
|
||||
value={inputValue.value}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onInput={saveNodeName}
|
||||
onBlur={() => (isShowEditNodeName.value = false)}
|
||||
onKeyup={keyupSaveNodeName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
// 保存节点名称
|
||||
const saveNodeName = (e: Event) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
inputValue.value = target.value;
|
||||
updateNode(tempNodeId.value, { name: inputValue.value });
|
||||
};
|
||||
|
||||
{/* 删除按钮 */}
|
||||
{isRemoved.value && (
|
||||
<span
|
||||
onClick={(ev) => removeFindNode(ev, tempNodeId.value, props.node)}
|
||||
class="flex items-center justify-center absolute top-[50%] right-[1rem] -mt-[.9rem]"
|
||||
>
|
||||
<SvgIcon class={styles.nodeClose} icon="close" color={isCondition.value ? '#333' : '#FFFFFF'} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{/* 节点主体 */}
|
||||
{!isCondition.value ? (
|
||||
<div class={[styles.nodeBody]}>
|
||||
{renderNodeContent.value &&
|
||||
h(renderNodeContent.value, {
|
||||
id: props.node.id,
|
||||
node: props.node || {},
|
||||
class: 'text-center',
|
||||
ref: nodeContentRef,
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
{/* 错误提示 */}
|
||||
{errorState.value.showTips && (
|
||||
<div class={styles.nodeErrorMsg}>
|
||||
<div class={styles.nodeErrorMsgBox}>
|
||||
<span onMouseenter={() => showErrorTips(true)} onMouseleave={() => showErrorTips(false)}>
|
||||
<SvgIcon class={styles.nodeErrorIcon} icon="tips" color="red" />
|
||||
</span>
|
||||
{errorState.value.message && <div class={styles.nodeErrorTips}>{errorState.value.message}</div>}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* 添加节点组件 */}
|
||||
<AddNode node={props.node} />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
// ====================== 渲染函数 ======================
|
||||
return () => (
|
||||
<div class={[styles.node, !isStart.value && styles.nodeArrows]}>
|
||||
<div
|
||||
class={[
|
||||
styles.nodeContent,
|
||||
isCondition.value && styles.nodeCondition,
|
||||
isExecuteResultBranch.value && styles.nodeExecuteResultBranch,
|
||||
]}
|
||||
onClick={handleNodeClick}
|
||||
>
|
||||
{/* 节点头部 */}
|
||||
<div
|
||||
class={[
|
||||
styles.nodeHeader,
|
||||
isCondition.value && styles.nodeConditionHeader,
|
||||
!typeIcon.value ? styles.nodeHeaderBranch : "",
|
||||
]}
|
||||
style={{
|
||||
color: config.value?.title?.color,
|
||||
backgroundColor: config.value?.title?.bgColor,
|
||||
}}
|
||||
>
|
||||
{/* 节点图标 */}
|
||||
{typeIcon.value ? (
|
||||
<SvgIcon
|
||||
icon={
|
||||
typeIcon.value
|
||||
? typeIcon.value
|
||||
: config.value?.icon?.name || ""
|
||||
}
|
||||
class={[
|
||||
styles.nodeIcon,
|
||||
"!absolute top-[50%] left-[1rem] -mt-[.8rem]",
|
||||
]}
|
||||
color={typeIconColor.value}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{/* 节点标题 */}
|
||||
<div class={styles.nodeHeaderTitle} title="点击编辑">
|
||||
<div class={styles.nodeHeaderTitleInput}>
|
||||
<input
|
||||
ref={nodeNameRef}
|
||||
value={inputValue.value}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onInput={saveNodeName}
|
||||
onBlur={() => (isShowEditNodeName.value = false)}
|
||||
onKeyup={keyupSaveNodeName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 删除按钮 */}
|
||||
{isRemoved.value && (
|
||||
<span
|
||||
onClick={(ev) =>
|
||||
removeFindNode(ev, tempNodeId.value, props.node)
|
||||
}
|
||||
class="flex items-center justify-center absolute top-[50%] right-[1rem] -mt-[.9rem]"
|
||||
>
|
||||
<SvgIcon
|
||||
class={styles.nodeClose}
|
||||
icon="close"
|
||||
color={closeIconColor.value}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{/* 节点主体 */}
|
||||
{!isCondition.value ? (
|
||||
<div class={[styles.nodeBody]}>
|
||||
{renderNodeContent.value &&
|
||||
h(renderNodeContent.value, {
|
||||
id: props.node.id,
|
||||
node: props.node || {},
|
||||
class: "text-center gradient-primary-txt",
|
||||
ref: nodeContentRef,
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
{/* 错误提示 */}
|
||||
{errorState.value.showTips && (
|
||||
<div class={styles.nodeErrorMsg}>
|
||||
<div class={styles.nodeErrorMsgBox}>
|
||||
<span
|
||||
onMouseenter={() => showErrorTips(true)}
|
||||
onMouseleave={() => showErrorTips(false)}
|
||||
>
|
||||
<SvgIcon
|
||||
class={styles.nodeErrorIcon}
|
||||
icon="tips"
|
||||
color="red"
|
||||
/>
|
||||
</span>
|
||||
{errorState.value.message && (
|
||||
<div class={styles.nodeErrorTips}>
|
||||
{errorState.value.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* 添加节点组件 */}
|
||||
<AddNode node={props.node} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -61,4 +61,30 @@
|
||||
|
||||
.flowConditionNodeAdd {
|
||||
@apply absolute left-1/2 -translate-x-1/2 -top-[15px] flex justify-center items-center z-[2] w-[70px] h-[30px] text-[12px] text-[#1c84c6] bg-white rounded-[20px] cursor-pointer shadow-md;
|
||||
}
|
||||
|
||||
:global(.defaultDark) {
|
||||
.flowNodeBranchCol {
|
||||
background-color: #161616;
|
||||
border-color: #D6A260;
|
||||
}
|
||||
.rightCoverLine,
|
||||
.leftCoverLine,
|
||||
.flowNodeBranchCol::before {
|
||||
background-color: #D6A260;
|
||||
}
|
||||
.topLeftCoverLine,
|
||||
.topRightCoverLine,
|
||||
.bottomLeftCoverLine,
|
||||
.bottomRightCoverLine {
|
||||
background-color: #161616;
|
||||
}
|
||||
.flowConditionNodeAdd {
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
background-image: linear-gradient(45deg, #FFF4DA 0%, #FFCD72 100%);
|
||||
}
|
||||
.multipleColumns {
|
||||
background-color: #161616!important;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import nodeOptions from '@components/FlowChart/lib/config'
|
||||
import { useStore } from '@components/FlowChart/useStore'
|
||||
import { CONDITION } from '@components/FlowChart/lib/alias'
|
||||
import { CONDITION, EXECUTE_RESULT_CONDITION } from '@components/FlowChart/lib/alias'
|
||||
|
||||
import NodeWrap from '@components/FlowChart/components/render/nodeWrap'
|
||||
import AddNode from '@components/FlowChart/components/other/addNode'
|
||||
import styles from '../branchNode/index.module.css'
|
||||
import type { BaseRenderNodeOptions, ExecuteResultBranchNodeData } from '@components/FlowChart/types'
|
||||
import type { BaseRenderNodeOptions, ExecuteResultBranchNodeData, ExecuteResultConditionNodeData } from '@components/FlowChart/types'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BranchNode',
|
||||
@@ -82,6 +82,7 @@ export default defineComponent({
|
||||
key={index}
|
||||
data-branch-index={index}
|
||||
data-branches-count={props.node.conditionNodes?.length}
|
||||
data-branch-type={condition.type === EXECUTE_RESULT_CONDITION && condition.config?.type ? condition.config.type : undefined}
|
||||
>
|
||||
{/* 条件节点 */}
|
||||
<NodeWrap node={condition} />
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import styles from './baseNode/index.module.css'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'EndNode',
|
||||
setup() {
|
||||
return () => (
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<div class="w-[1.5rem] h-[1.5rem] rounded-[1rem] bg-[#cacaca]"></div>
|
||||
<div class="text-[#5a5e66] mb-[10rem]">流程结束</div>
|
||||
<div class={styles.endNode}></div>
|
||||
<div class={styles.endNodeTxt}>流程结束</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -126,4 +126,27 @@
|
||||
.addRight::before {
|
||||
left: -2rem;
|
||||
border-color: transparent #FFFFFF transparent transparent;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.defaultDark) {
|
||||
.add::before {
|
||||
background-color: #D6A260;
|
||||
}
|
||||
.addBtn {
|
||||
background-color: transparent;
|
||||
background-image: linear-gradient(45deg, #FFF4DA 0%, #FFCD72 100%)!important;
|
||||
margin-top: -1.2rem;
|
||||
}
|
||||
.addSelectBox {
|
||||
background-color: #48484E;
|
||||
.addSelectItem{
|
||||
color: #fff;
|
||||
&:hover {
|
||||
background-color: #1a1a1a !important;
|
||||
}
|
||||
}
|
||||
&::before {
|
||||
border-color: transparent #48484E transparent transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useAddNodeController } from '@components/FlowChart/useController'
|
||||
import SvgIcon from '@components/SvgIcon'
|
||||
import styles from './index.module.css'
|
||||
import { useTheme } from "@baota/naive-ui/theme";
|
||||
|
||||
import type {
|
||||
NodeIcon,
|
||||
@@ -41,7 +42,7 @@ export default defineComponent({
|
||||
} = useAddNodeController()
|
||||
|
||||
const config = ref<BaseRenderNodeOptions<BaseNodeData | BranchNodeData>>() // 节点配置
|
||||
|
||||
const { isDark } = useTheme();
|
||||
watch(
|
||||
() => props.node.type,
|
||||
(newVal) => {
|
||||
@@ -50,44 +51,58 @@ export default defineComponent({
|
||||
)
|
||||
|
||||
return () => (
|
||||
<div class={styles.add}>
|
||||
<div
|
||||
ref={addNodeBtnRef}
|
||||
class={styles.addBtn}
|
||||
onMouseenter={() => showNodeSelect(true, props.node.type as NodeNum)}
|
||||
onMouseleave={() => showNodeSelect(false)}
|
||||
>
|
||||
<SvgIcon icon="plus" class={styles.addBtnIcon} color="#FFFFFF" />
|
||||
{isShowAddNodeSelect.value && (
|
||||
<ul
|
||||
ref={addNodeSelectRef}
|
||||
class={[styles.addSelectBox, addNodeSelectPostion.value === 1 ? styles.addLeft : styles.addRight]}
|
||||
>
|
||||
{nodeSelectList.value.map((item: NodeSelect) => {
|
||||
// 判断类型是否支持添加
|
||||
if (!excludeNodeSelectList.value?.includes(item.type)) {
|
||||
return (
|
||||
<li
|
||||
key={item.type}
|
||||
class={[styles.addSelectItem, item.selected && styles.addSelected]}
|
||||
onClick={() => addNodeData(props.node, item.type)}
|
||||
onMouseenter={itemNodeSelected}
|
||||
>
|
||||
<SvgIcon
|
||||
icon={'flow-' + item.icon.name}
|
||||
class={styles.addSelectItemIcon}
|
||||
color={item.selected ? '#FFFFFF' : item.icon.color}
|
||||
/>
|
||||
<div class={styles.addSelectItemTitle}>{item.title.name}</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
<div class={styles.add}>
|
||||
<div
|
||||
ref={addNodeBtnRef}
|
||||
class={styles.addBtn}
|
||||
onMouseenter={() => showNodeSelect(true, props.node.type as NodeNum)}
|
||||
onMouseleave={() => showNodeSelect(false)}
|
||||
>
|
||||
<SvgIcon
|
||||
icon="plus"
|
||||
class={styles.addBtnIcon}
|
||||
color={isDark.value ? "#000" : "#FFFFFF"}
|
||||
/>
|
||||
{isShowAddNodeSelect.value && (
|
||||
<ul
|
||||
ref={addNodeSelectRef}
|
||||
class={[
|
||||
styles.addSelectBox,
|
||||
addNodeSelectPostion.value === 1
|
||||
? styles.addLeft
|
||||
: styles.addRight,
|
||||
]}
|
||||
>
|
||||
{nodeSelectList.value.map((item: NodeSelect) => {
|
||||
// 判断类型是否支持添加
|
||||
if (!excludeNodeSelectList.value?.includes(item.type)) {
|
||||
return (
|
||||
<li
|
||||
key={item.type}
|
||||
class={[
|
||||
styles.addSelectItem,
|
||||
item.selected && styles.addSelected,
|
||||
]}
|
||||
onClick={() => addNodeData(props.node, item.type)}
|
||||
onMouseenter={itemNodeSelected}
|
||||
>
|
||||
<SvgIcon
|
||||
icon={"flow-" + item.icon.name}
|
||||
class={styles.addSelectItemIcon}
|
||||
color={item.selected ? "#FFFFFF" : item.icon.color}
|
||||
/>
|
||||
<div class={styles.addSelectItemTitle}>
|
||||
{item.title.name}
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
})
|
||||
|
||||
@@ -17,8 +17,58 @@
|
||||
}
|
||||
|
||||
.flowZoomIcon {
|
||||
@apply w-[2.5rem] h-[2.5rem] cursor-pointer border flex items-center justify-center;
|
||||
@apply w-[2.5rem] h-[2.5rem] cursor-pointer border flex items-center justify-center rounded-md;
|
||||
border-color: var(--n-border-color);
|
||||
transition: background-color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.flowZoomIcon:hover {
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.flowZoomValue {
|
||||
@apply text-[1.4rem] font-medium px-4;
|
||||
color: var(--n-text-color-2);
|
||||
}
|
||||
|
||||
.flowZoomDark {
|
||||
width: auto;
|
||||
min-width: 18rem;
|
||||
height: 4.2rem;
|
||||
border-radius: 8px;
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 12px 20px rgba(0, 0, 0, 0.2);
|
||||
gap: 0;
|
||||
color: #fff;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.flowZoomDark > * {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flowZoomIconDark {
|
||||
border: none;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
padding: 0 1.2rem;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.flowZoomIconDark:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.flowZoomValueDark {
|
||||
color: #fff;
|
||||
background-color: #232323;
|
||||
font-weight: 600;
|
||||
min-width: 4.8rem;
|
||||
padding: 0 1.6rem;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* 嵌套节点包装器样式 */
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { NButton, NIcon, NInput } from 'naive-ui'
|
||||
import { useTheme } from "@baota/naive-ui/theme";
|
||||
import { SaveOutlined, ArrowLeftOutlined, ReloadOutlined } from "@vicons/antd";
|
||||
import { $t } from '@locales/index'
|
||||
import SvgIcon from '@components/SvgIcon'
|
||||
@@ -35,6 +36,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props: FlowNodeProps, { slots }) {
|
||||
const { isDark } = useTheme()
|
||||
const cssVars = useThemeCssVar([
|
||||
"borderColor",
|
||||
"dividerColor",
|
||||
@@ -195,11 +197,15 @@ export default defineComponent({
|
||||
wheelTimeout = null;
|
||||
}
|
||||
});
|
||||
const zoomIconColor = computed(() =>
|
||||
isDark.value ? "#ffffff" : "#5a5e66"
|
||||
);
|
||||
|
||||
return () => (
|
||||
<div class="flex flex-col w-full h-full" style={cssVars.value}>
|
||||
<div class="w-full h-[6rem] px-[2rem] mb-[2rem] rounded-lg flex items-center gap-2 justify-between">
|
||||
<div class="flex items-center">
|
||||
<NButton onClick={goBack}>
|
||||
<NButton class="gradient-default-btn" onClick={goBack}>
|
||||
<NIcon class="mr-1">
|
||||
<ArrowLeftOutlined />
|
||||
</NIcon>
|
||||
@@ -210,12 +216,12 @@ export default defineComponent({
|
||||
<NInput
|
||||
v-model:value={flowData.value.name}
|
||||
placeholder={$t("t_0_1745490735213")}
|
||||
class="!w-[30rem] !border-none "
|
||||
class="!w-[30rem] !border-none !bg-[var(--workflow-header-input-bg)]"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<NButton
|
||||
type="primary"
|
||||
type={isDark.value ? "tertiary" : "primary"}
|
||||
onClick={handleSaveConfig}
|
||||
disabled={!selectedNodeId}
|
||||
>
|
||||
@@ -254,28 +260,50 @@ export default defineComponent({
|
||||
</div>
|
||||
</div>
|
||||
{/* 缩放控制区 */}
|
||||
<div class={styles.flowZoom}>
|
||||
<div class={styles.flowZoomIcon} onClick={() => handleZoom(1)}>
|
||||
<div class={[styles.flowZoom, isDark.value ? styles.flowZoomDark : ""]}>
|
||||
<div
|
||||
class={[
|
||||
styles.flowZoomIcon,
|
||||
isDark.value ? styles.flowZoomIconDark : "",
|
||||
]}
|
||||
onClick={() => handleZoom(1)}
|
||||
>
|
||||
<SvgIcon
|
||||
icon="subtract"
|
||||
class={`${flowZoom.value === 50 ? styles.disabled : ""}`}
|
||||
color="#5a5e66"
|
||||
/>
|
||||
</div>
|
||||
<span>{flowZoom.value}%</span>
|
||||
<div class={styles.flowZoomIcon} onClick={() => handleZoom(2)}>
|
||||
<SvgIcon
|
||||
icon="plus"
|
||||
class={`${flowZoom.value === 300 ? styles.disabled : ""}`}
|
||||
color="#5a5e66"
|
||||
color={zoomIconColor.value}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class={styles.flowZoomIcon}
|
||||
class={[
|
||||
styles.flowZoomValue,
|
||||
isDark.value ? styles.flowZoomValueDark : "",
|
||||
]}
|
||||
>
|
||||
{flowZoom.value}%
|
||||
</div>
|
||||
<div
|
||||
class={[
|
||||
styles.flowZoomIcon,
|
||||
isDark.value ? styles.flowZoomIconDark : "",
|
||||
]}
|
||||
onClick={() => handleZoom(2)}
|
||||
>
|
||||
<SvgIcon
|
||||
icon="plus"
|
||||
class={`${flowZoom.value === 300 ? styles.disabled : ""}`}
|
||||
color={zoomIconColor.value}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class={[
|
||||
styles.flowZoomIcon,
|
||||
isDark.value ? styles.flowZoomIconDark : "",
|
||||
]}
|
||||
onClick={handleReset}
|
||||
title="重置视图"
|
||||
>
|
||||
<NIcon size="16" color="#5a5e66">
|
||||
<NIcon size="16" color={zoomIconColor.value}>
|
||||
<ReloadOutlined />
|
||||
</NIcon>
|
||||
</div>
|
||||
|
||||
@@ -213,10 +213,10 @@ export default defineComponent({
|
||||
<NGi span={11}>
|
||||
<div class="flex items-center h-full">
|
||||
<NDivider vertical />
|
||||
<NButton class="mx-[8px]" onClick={goToAddNotifyProvider} ghost>
|
||||
<NButton class="flex-1 table-action-btn mx-[8px]" onClick={goToAddNotifyProvider} ghost>
|
||||
{$t('t_2_1745887834248')}
|
||||
</NButton>
|
||||
<NButton onClick={fetchNotifyProviderData} ghost>
|
||||
<NButton class="table-action-btn" onClick={fetchNotifyProviderData} ghost>
|
||||
{$t('t_0_1746497662220')}
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
141
frontend/apps/allin-ssl/src/config/theme.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import type { GlobalThemeOverrides } from 'naive-ui'
|
||||
|
||||
/**
|
||||
* AllinSSL 自定义暗色主题配置
|
||||
* 覆盖全局主题变量
|
||||
*/
|
||||
export const allinSslDarkThemeOverrides: GlobalThemeOverrides = {
|
||||
common: {
|
||||
bodyColor: "#000", // 页面主体背景色 (对应 --n-color)
|
||||
cardColor: "#000", // 卡片背景色
|
||||
modalColor: "#000", // 弹出框背景色 (--n-color-modal)
|
||||
hoverColor: "#1a1a1a", // 悬停背景色
|
||||
errorColor: "#FF4314",
|
||||
successColor: "#16D13B",
|
||||
},
|
||||
Card: {
|
||||
color: "#000",
|
||||
closeIconColor: "#fff",
|
||||
},
|
||||
Modal: {
|
||||
color: "#000",
|
||||
closeIconColor: "#fff",
|
||||
borderColorHover: "#000",
|
||||
},
|
||||
Layout: {
|
||||
headerColor: "#000", // 头部背景色
|
||||
siderColor: "#000", // 侧边栏背景色
|
||||
footerColor: "#000", // 底部背景色
|
||||
},
|
||||
DataTable: {
|
||||
thColor: "#202020", // 普通表格表头背景色 (--n-th-color)
|
||||
thColorModal: "#202020", // 弹窗内表格表头背景色 (--n-th-color-modal)
|
||||
borderColorModal: "#202020",
|
||||
loadingColor: "transparent",
|
||||
},
|
||||
// 仅覆盖暗色主题下 NTag 的 error 语义色为 #FF4314
|
||||
Tag: {
|
||||
color: "transparent",
|
||||
colorInfo: "transparent",
|
||||
border: "1px solid #fff",
|
||||
borderError: "1px solid #FF4314",
|
||||
borderSuccess: "1px solid #16D13B",
|
||||
borderWarning: "1px solid #FF8E22",
|
||||
borderInfo: "1px solid #1EA6FF",
|
||||
textColorInfo: "#1EA6FF",
|
||||
textColorWarning: "#FF8E22",
|
||||
},
|
||||
// 侧边栏菜单激活项样式
|
||||
Menu: {
|
||||
itemColorActive: "#282218", // 激活项背景色
|
||||
itemTextColorActive: "transparent", // 文字颜色设为透明,通过 CSS 渐变实现
|
||||
itemIconColorActive: "transparent", // 图标颜色设为透明,通过 CSS 渐变实现
|
||||
arrowColorActive: "transparent", // 箭头颜色设为透明,通过 CSS 渐变实现
|
||||
itemTextColorChildActive: "transparent",
|
||||
itemTextColorChildActiveHover: "transparent",
|
||||
},
|
||||
Pagination: {
|
||||
itemTextColorHover: "#FFCF76",
|
||||
itemTextColorPressed: "#FFCF76",
|
||||
itemTextColorActive: "transparent",
|
||||
itemBorderActive: "1px solid transparent",
|
||||
itemBorder: "1px solid transparent",
|
||||
},
|
||||
Tabs: {
|
||||
tabTextColorHover: "#fff",
|
||||
tabTextColorActive: "#fff",
|
||||
tabColorSegment: "#000",
|
||||
},
|
||||
InternalSelection: {
|
||||
border: "1px solid #4e4e4e",
|
||||
borderHover: "1px solid #4e4e4e",
|
||||
borderActive: "1px solid #4e4e4e",
|
||||
borderFocus: "1px solid #4e4e4e",
|
||||
colorActive: "#171717",
|
||||
boxShadowHover: "0 0 8px 2px rgba(78, 78, 78, 0.3)",
|
||||
boxShadowActive: "0 0 8px 2px rgba(78, 78, 78, 0.3)",
|
||||
boxShadowFocus: "0 0 8px 2px rgba(78, 78, 78, 0.3)",
|
||||
caretColor: "#fff",
|
||||
},
|
||||
InternalSelectMenu: {
|
||||
optionTextColorHover: "#fff",
|
||||
optionTextColorActive: "#FFCF76",
|
||||
optionTextColorPressed: "#fff",
|
||||
optionCheckColor: "#fff",
|
||||
},
|
||||
Button: {
|
||||
colorDefault: "#2E2D2D",
|
||||
colorFocus: "#2E2D2D", // 聚焦时背景色设为透明
|
||||
colorHoverPrimary: "transparent",
|
||||
colorPressedPrimary: "transparent",
|
||||
textColorFocus: "#fff", // 聚焦时文字颜色设为白色
|
||||
rippleColor: "#fff",
|
||||
rippleColorPrimary: "#9C6240",
|
||||
textColorPressed: "#fff",
|
||||
colorPressed: "#202020",
|
||||
},
|
||||
Input: {
|
||||
color: "#171717",
|
||||
colorHover: "transparent",
|
||||
colorFocus: "#171717", // 聚焦时背景色设为透明
|
||||
border: "none", // 默认去掉边框
|
||||
borderColor: "transparent",
|
||||
borderHover: "none",
|
||||
borderHoverError: "1px solid var(--n-error-primary-color)",
|
||||
borderFocusError: "1px solid var(--n-error-primary-color)",
|
||||
borderFocus: "none",
|
||||
caretColor: "#fff",
|
||||
boxShadowFocus: "0 0 8px 2px rgba(0, 0, 0, 0.3)", // 聚焦时柔和的红色发光效果
|
||||
},
|
||||
Switch: {
|
||||
railColorActive: "transparent",
|
||||
boxShadowFocus: "0 0 8px 0 rgba(255, 255, 255, 0.3)",
|
||||
},
|
||||
Dialog: {
|
||||
color: "#000",
|
||||
titleTextColor: "#fff",
|
||||
closeIconColor: "#fff",
|
||||
textColor: "#979BA5",
|
||||
iconColor: "transparent",
|
||||
},
|
||||
Dropdown: {
|
||||
color: "#171717",
|
||||
},
|
||||
Checkbox: {
|
||||
borderChecked: "transparent",
|
||||
colorChecked: "transparent",
|
||||
},
|
||||
Popover: {
|
||||
color: "#000",
|
||||
textColor: "#fff",
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* AllinSSL 自定义亮色主题配置
|
||||
* 如果需要亮色主题也覆盖,可以在这里配置
|
||||
*/
|
||||
export const allinSslLightThemeOverrides: GlobalThemeOverrides = {
|
||||
// 亮色主题暂不需要覆盖
|
||||
}
|
||||
|
||||
@@ -688,5 +688,6 @@
|
||||
"t_15_1752724141047": "تنزيل القالب",
|
||||
"t_16_1752724141914": "تحميل قالب الاستيراد",
|
||||
"t_17_1752724150341": "قم بتحميل ملف القالب القياسي، ثم تحريره وتحميله للوارد",
|
||||
"t_0_1756203020746": "الهيئة الخاصة"
|
||||
"t_0_1756203020746": "الهيئة الخاصة",
|
||||
"t_0_1763542847861": "الرجاء إدخال اسم الشهادة للبحث"
|
||||
}
|
||||
@@ -688,5 +688,6 @@
|
||||
"t_15_1752724141047": "Download template",
|
||||
"t_16_1752724141914": "Download import template",
|
||||
"t_17_1752724150341": "Download the standard template file, edit it, and then upload to import",
|
||||
"t_0_1756203020746": "Private CA"
|
||||
"t_0_1756203020746": "Private CA",
|
||||
"t_0_1763542847861": "Please enter the certificate name to search"
|
||||
}
|
||||
@@ -688,5 +688,6 @@
|
||||
"t_15_1752724141047": "Descargar plantilla",
|
||||
"t_16_1752724141914": "Descargar plantilla de importación",
|
||||
"t_17_1752724150341": "Descargue el archivo de plantilla estándar, edítelo y luego cárguelo para importarlo",
|
||||
"t_0_1756203020746": "CA privado"
|
||||
"t_0_1756203020746": "CA privado",
|
||||
"t_0_1763542847861": "Ingrese el nombre del certificado para buscar"
|
||||
}
|
||||
@@ -688,5 +688,6 @@
|
||||
"t_15_1752724141047": "Télécharger le modèle",
|
||||
"t_16_1752724141914": "Télécharger le modèle d'importation",
|
||||
"t_17_1752724150341": "Téléchargez le fichier modèle standard, éditez-le, puis téléchargez-le pour l'importer",
|
||||
"t_0_1756203020746": "AC privée"
|
||||
"t_0_1756203020746": "AC privée",
|
||||
"t_0_1763542847861": "Veuillez entrer le nom du certificat pour rechercher"
|
||||
}
|
||||
@@ -688,5 +688,6 @@
|
||||
"t_15_1752724141047": "テンプレートをダウンロード",
|
||||
"t_16_1752724141914": "インポートテンプレートをダウンロード",
|
||||
"t_17_1752724150341": "標準テンプレートファイルをダウンロードし、編集してからアップロードしてインポートします",
|
||||
"t_0_1756203020746": "プライベートCA"
|
||||
"t_0_1756203020746": "プライベートCA",
|
||||
"t_0_1763542847861": "証明書の名前を入力して検索してください"
|
||||
}
|
||||
@@ -688,5 +688,6 @@
|
||||
"t_15_1752724141047": "템플릿 다운로드",
|
||||
"t_16_1752724141914": "수입 템플릿 다운로드",
|
||||
"t_17_1752724150341": "표준 템플릿 파일을 다운로드하여 편집한 후 업로드하여 가져옵니다",
|
||||
"t_0_1756203020746": "개인 CA"
|
||||
"t_0_1756203020746": "개인 CA",
|
||||
"t_0_1763542847861": "인증서 이름을 입력하여 검색하세요"
|
||||
}
|
||||
@@ -688,5 +688,6 @@
|
||||
"t_15_1752724141047": "Baixar modelo",
|
||||
"t_16_1752724141914": "Baixar modelo de importação",
|
||||
"t_17_1752724150341": "Baixe o arquivo modelo padrão, edite-o e, em seguida, faça o upload para importar",
|
||||
"t_0_1756203020746": "CA Privado"
|
||||
"t_0_1756203020746": "CA Privado",
|
||||
"t_0_1763542847861": "Por favor, insira o nome do certificado para pesquisar"
|
||||
}
|
||||
@@ -688,5 +688,6 @@
|
||||
"t_15_1752724141047": "Скачать шаблон",
|
||||
"t_16_1752724141914": "Скачать шаблон для импорта",
|
||||
"t_17_1752724150341": "Скачайте стандартный шаблон файла, отредактируйте его, а затем загрузите для импорта",
|
||||
"t_0_1756203020746": "Приватный УЦ"
|
||||
"t_0_1756203020746": "Приватный УЦ",
|
||||
"t_0_1763542847861": "Введите название сертификата для поиска"
|
||||
}
|
||||
@@ -688,5 +688,6 @@
|
||||
"t_15_1752724141047": "下载模板",
|
||||
"t_16_1752724141914": "下载导入模板",
|
||||
"t_17_1752724150341": "下载标准模板文件,编辑后上传导入",
|
||||
"t_0_1756203020746": "私有CA"
|
||||
"t_0_1756203020746": "私有CA",
|
||||
"t_0_1763542847861": "请输入证书名称搜索"
|
||||
}
|
||||
@@ -687,5 +687,6 @@
|
||||
"t_15_1752724141047": "下載範本",
|
||||
"t_16_1752724141914": "下載匯入模板",
|
||||
"t_17_1752724150341": "下載標準範本檔案,編輯後上傳匯入",
|
||||
"t_0_1756203020746": "私有CA"
|
||||
"t_0_1756203020746": "私有CA",
|
||||
"t_0_1763542847861": "請輸入憑證名稱搜尋"
|
||||
}
|
||||
@@ -44,7 +44,8 @@
|
||||
background-color: var(--n-merged-td-color);
|
||||
}
|
||||
|
||||
.n-scrollbar > .n-scrollbar-rail.n-scrollbar-rail--horizontal--top, .n-scrollbar + .n-scrollbar-rail.n-scrollbar-rail--horizontal--top {
|
||||
.n-scrollbar > .n-scrollbar-rail.n-scrollbar-rail--horizontal--top,
|
||||
.n-scrollbar + .n-scrollbar-rail.n-scrollbar-rail--horizontal--top {
|
||||
top: 40px !important;
|
||||
}
|
||||
|
||||
@@ -52,3 +53,756 @@
|
||||
height: 3.2rem;
|
||||
line-height: 3.2rem;
|
||||
}
|
||||
|
||||
/* 暗色主题下菜单激活项样式 */
|
||||
.defaultDark {
|
||||
.n-menu {
|
||||
.n-submenu {
|
||||
.n-menu-item-content-header {
|
||||
background: var(--menu-active-gradient) !important;
|
||||
-webkit-background-clip: text !important;
|
||||
}
|
||||
.n-menu-item-content--child-active{
|
||||
.n-menu-item-content__icon,
|
||||
.n-menu-item-content__arrow{
|
||||
color: transparent !important;
|
||||
svg path {
|
||||
fill: url(#menu-active-icon-gradient) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.n-menu-item-content.n-menu-item-content--selected {
|
||||
position: relative;
|
||||
background-color: transparent !important;
|
||||
border-radius: var(--n-border-radius, 12px);
|
||||
z-index: 0;
|
||||
|
||||
.n-menu-item-content-header a {
|
||||
background: var(--menu-active-gradient) !important;
|
||||
-webkit-background-clip: text !important;
|
||||
background-clip: text !important;
|
||||
-webkit-text-fill-color: transparent !important;
|
||||
color: transparent !important;
|
||||
}
|
||||
|
||||
.n-menu-item-content__icon {
|
||||
background: var(--menu-active-gradient) !important;
|
||||
-webkit-background-clip: text !important;
|
||||
background-clip: text !important;
|
||||
-webkit-text-fill-color: transparent !important;
|
||||
color: transparent !important;
|
||||
|
||||
svg path {
|
||||
fill: url(#menu-active-icon-gradient) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
border-radius: inherit;
|
||||
padding: 1px;
|
||||
background: var(--menu-active-gradient);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 9px;
|
||||
right: 9px;
|
||||
top: 1px;
|
||||
bottom: 1px;
|
||||
border-radius: calc(var(--n-border-radius, 12px) - 2px);
|
||||
background-color: #282218;
|
||||
z-index: -2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.gradient-primary-btn,
|
||||
.n-modal .n-card__footer .n-button--primary-type,
|
||||
.n-dialog .n-dialog__action .n-button--primary-type,
|
||||
.n-badge .n-badge-sup{
|
||||
background-image: linear-gradient(90deg, #9c6240 0%, #ffcf76 100%)!important;
|
||||
border: none!important;
|
||||
box-shadow: none!important;
|
||||
font-weight: 600;
|
||||
&:active,
|
||||
&:hover {
|
||||
background-image: linear-gradient(90deg, #af724a 0%, #ffd98e 100%)!important;
|
||||
}
|
||||
&:disabled, &.n-button--disabled {
|
||||
background-image: none;
|
||||
background-color: rgba(156, 98, 64, 0.35);
|
||||
color: rgba(44, 21, 6, 0.45);
|
||||
}
|
||||
.n-button__border,
|
||||
.n-button__state-border {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.gradient-primary-txt,
|
||||
.n-descriptions-table .n-button__content {
|
||||
background: var(--menu-active-gradient) !important;
|
||||
-webkit-background-clip: text !important;
|
||||
background-clip: text !important;
|
||||
-webkit-text-fill-color: transparent !important;
|
||||
color: transparent !important;
|
||||
}
|
||||
.gradient-primary-btn .n-button__content {
|
||||
color: #000!important;
|
||||
}
|
||||
.n-tag.tag-default {
|
||||
border: 1px solid #fff;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.n-tag.tag-info {
|
||||
border: 1px solid #1EA6FF;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.gradient-default-btn,
|
||||
.n-modal .n-button--default-type {
|
||||
background-color: #2E2D2D;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
&:hover {
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
}
|
||||
.n-button__border,
|
||||
.n-button__state-border {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
/* 模态框样式 */
|
||||
.n-modal {
|
||||
background-color: #171717;
|
||||
.n-data-table-empty {
|
||||
background-color: #000;
|
||||
}
|
||||
.n-card {
|
||||
background-color: #171717;
|
||||
}
|
||||
.n-card-header__main {
|
||||
font-weight: 600;
|
||||
}
|
||||
.n-input {
|
||||
textarea {
|
||||
caret-color: #fff!important;
|
||||
}
|
||||
&.n-input--focus {
|
||||
background-color: #2f2f2f;
|
||||
.n-input__state-border {
|
||||
box-shadow: 0 0 8px 2px rgba(78, 78, 78, 0.3)!important;
|
||||
}
|
||||
}
|
||||
.n-input__state-border {
|
||||
border-color: transparent;
|
||||
}
|
||||
&.n-input--error-status {
|
||||
.n-input__state-border {
|
||||
border-color: var(--n-error-primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.n-button--primary-type .n-base-wave.n-base-wave--active {
|
||||
box-shadow: #9C6240 0 0 5px 0!important;
|
||||
}
|
||||
.n-button--default-type .n-base-wave.n-base-wave--active {
|
||||
box-shadow: #202020 0 0 5px 0!important;
|
||||
}
|
||||
.n-base-icon {
|
||||
color: #fff!important;
|
||||
}
|
||||
.n-data-table-wrapper {
|
||||
border-width: 2px;
|
||||
border-radius: 0;
|
||||
border-color: #202020;
|
||||
}
|
||||
.n-data-table-tr {
|
||||
&:hover {
|
||||
.n-data-table-td {
|
||||
background-color: #0f0f0f;
|
||||
}
|
||||
}
|
||||
.n-data-table-td {
|
||||
border-color: #202020;
|
||||
background-color: #000;
|
||||
}
|
||||
}
|
||||
.n-data-table-th {
|
||||
border-color: #202020;
|
||||
background-color: #202020;
|
||||
}
|
||||
.n-base-select-menu {
|
||||
.n-base-select-option--pending::before {
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
.n-base-select-option{
|
||||
color: #fff;
|
||||
}
|
||||
.n-base-select-option:active,
|
||||
.n-base-select-option--selected .n-base-select-option__content {
|
||||
color: #ffcf76;
|
||||
}
|
||||
}
|
||||
.n-select {
|
||||
.n-base-selection-label {
|
||||
border: 1px solid #4e4e4e;
|
||||
background-color: #171717;
|
||||
.n-base-selection-input {
|
||||
caret-color: #fff!important;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.n-base-selection__state-border{
|
||||
box-shadow: 0 0 8px 2px rgba(78, 78, 78, 0.3);
|
||||
}
|
||||
}
|
||||
.n-base-selection--focus,
|
||||
.n-base-selection--active {
|
||||
.n-base-selection__state-border{
|
||||
box-shadow: 0 0 8px 2px rgba(78, 78, 78, 0.3);
|
||||
border-color: transparent;
|
||||
}
|
||||
.n-base-selection-tags {
|
||||
background-color: #2f2f2f!important;
|
||||
}
|
||||
}
|
||||
.n-base-selection__state-border{
|
||||
border-color: transparent;
|
||||
}
|
||||
.n-base-selection--error-status,
|
||||
.n-base-selection--error-status.n-base-selection--focus{
|
||||
.n-base-selection-label {
|
||||
background-color: transparent!important;
|
||||
}
|
||||
.n-base-selection__state-border{
|
||||
border-color: var(--n-error-primary-color)!important;
|
||||
}
|
||||
}
|
||||
.n-base-selection--disabled .n-base-selection-label{
|
||||
opacity: .7;
|
||||
}
|
||||
}
|
||||
.n-pagination-item:hover,
|
||||
.n-pagination-item:active{
|
||||
color: #ffcf76;
|
||||
}
|
||||
.n-pagination-item.n-pagination-item--active {
|
||||
border: 0;
|
||||
}
|
||||
.n-input--error-status .n-input__input-el {
|
||||
caret-color: var(--n-error-primary-color);
|
||||
}
|
||||
.n-input__input-el {
|
||||
caret-color: #fff;
|
||||
}
|
||||
.n-form-item-label__asterisk {
|
||||
color: #ffcf76;
|
||||
}
|
||||
.n-form-item-feedback__line {
|
||||
color: var(--n-error-primary-color);
|
||||
}
|
||||
.n-tag {
|
||||
border: 1px solid var(--n-success-status-color);
|
||||
color: var(--n-success-status-color);
|
||||
&.tag-default {
|
||||
background-color: transparent;
|
||||
border-color: #fff;
|
||||
color: #fff;
|
||||
}
|
||||
&.error {
|
||||
border-color: var(--n-error-primary-color);
|
||||
color: var(--n-error-primary-color);
|
||||
}
|
||||
&.tag-info {
|
||||
background-color: transparent;
|
||||
border-color: #1EA6FF;
|
||||
color: #1EA6FF;
|
||||
}
|
||||
.n-tag__border {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.n-radio {
|
||||
.n-radio__dot {
|
||||
&::before {
|
||||
background-image: linear-gradient(90deg, #9c6240 0%, #ffcf76 100%)!important;
|
||||
}
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
border-radius: inherit;
|
||||
padding: 1px;
|
||||
background: linear-gradient(90deg, #9c6240 0%, #ffcf76 100%);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
}
|
||||
&:not(.n-radio--disabled):hover .n-radio__dot,
|
||||
.n-radio__dot.n-radio__dot--checked,
|
||||
&:focus-within .n-radio__dot {
|
||||
box-shadow: none!important;
|
||||
&::after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.n-switch {
|
||||
&:focus .n-switch__rail,
|
||||
.n-switch__rail:active {
|
||||
box-shadow: 0 0 8px 0 rgba(255, 255, 255, 0.3)!important;
|
||||
}
|
||||
&.n-switch--active .n-switch__rail {
|
||||
background-color: #454545;
|
||||
background-image: linear-gradient(90deg, #9c6240 0%, #ffcf76 100%)!important;
|
||||
}
|
||||
}
|
||||
.n-tabs{
|
||||
&.n-tabs--bar-type .n-tabs-tab:not(.n-tabs-tab--active):hover {
|
||||
transition: opacity 0.3s ease, opacity 0.3s ease;
|
||||
color: #fff;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
.n-upload-dragger:hover {
|
||||
border-color: #fff!important;
|
||||
}
|
||||
.workflow-template-card {
|
||||
position: relative;
|
||||
border-color: #8F8F8F;
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
padding: 1px;
|
||||
background: var(--menu-active-gradient);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
opacity: 0;
|
||||
transition: opacity 1s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
&.selected {
|
||||
border-color: transparent;
|
||||
&::before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.n-divider {
|
||||
.n-divider__title span{
|
||||
color: #fff!important;
|
||||
}
|
||||
.n-icon {
|
||||
color: #fff!important;
|
||||
}
|
||||
}
|
||||
.n-steps {
|
||||
.n-step-splitor {
|
||||
background: var(--menu-active-gradient);
|
||||
}
|
||||
.n-step-indicator {
|
||||
box-shadow: none;
|
||||
}
|
||||
.n-step--finish-status {
|
||||
.n-step-indicator {
|
||||
position: relative;
|
||||
border: none;
|
||||
.n-base-icon {
|
||||
svg path {
|
||||
fill: url(#menu-active-icon-gradient) !important;
|
||||
}
|
||||
}
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
padding: 1px;
|
||||
background: var(--menu-active-gradient);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.n-step--process-status {
|
||||
.n-step-indicator {
|
||||
background: var(--menu-active-gradient);
|
||||
color: #000;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
.n-step--wait-status .n-step-indicator{
|
||||
box-shadow: 0 0 0 1px #6d6d6d;
|
||||
}
|
||||
}
|
||||
.n-base-loading__container {
|
||||
svg circle:first-of-type {
|
||||
stroke: transparent !important;
|
||||
}
|
||||
svg circle:last-of-type {
|
||||
fill: none !important;
|
||||
stroke: url(#menu-active-icon-gradient) !important;
|
||||
stroke-width: 10px !important;
|
||||
stroke-linecap: round;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
.n-radio-group {
|
||||
.n-radio-group__splitor--checked {
|
||||
background-color: #4e4e4e;
|
||||
}
|
||||
}
|
||||
.n-radio-button {
|
||||
.n-radio-button__state-border {
|
||||
display: none!important;
|
||||
}
|
||||
background-color: transparent;
|
||||
transition: color 0.3s ease, border-color 0.3s ease;
|
||||
|
||||
.n-radio-button__label {
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.n-radio-button__state-border {
|
||||
border-color: rgba(255, 255, 255, 0.25);
|
||||
border-radius: inherit;
|
||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
&:hover:not(.n-radio-button--checked):not(.n-radio-button--disabled) {
|
||||
color: #fff;
|
||||
transition: box-shadow 0.3s ease-in-out;
|
||||
box-shadow: 0 0 6px rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
&.n-radio-button--focus {
|
||||
.n-radio-button__state-border {
|
||||
display: none!important;
|
||||
}
|
||||
}
|
||||
|
||||
&.n-radio-button--checked {
|
||||
background-image: var(--menu-active-gradient);
|
||||
color: #000;
|
||||
border: 0;
|
||||
.n-radio-button__label {
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 卡片 */
|
||||
.n-card {
|
||||
.n-code{
|
||||
.hljs-date-text,
|
||||
.hljs-info-text {
|
||||
color: var(--n-success-status-color)!important;
|
||||
}
|
||||
.hljs-error-text {
|
||||
color: var(--n-error-primary-color)!important;
|
||||
}
|
||||
}
|
||||
.n-alert {
|
||||
.n-base-icon{
|
||||
position: relative;
|
||||
background-color: transparent !important;
|
||||
svg,
|
||||
svg path {
|
||||
fill: url(#menu-active-icon-gradient) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 表格操作列按钮 - 渐变边框和字体 */
|
||||
.table-action-btn {
|
||||
position: relative;
|
||||
background-color: transparent!important;
|
||||
z-index: 0;
|
||||
padding: 0 0.8rem;
|
||||
/* 渐变字体 */
|
||||
.n-button__content {
|
||||
background: var(--menu-active-gradient) !important;
|
||||
-webkit-background-clip: text !important;
|
||||
background-clip: text !important;
|
||||
-webkit-text-fill-color: transparent !important;
|
||||
color: transparent !important;
|
||||
}
|
||||
|
||||
/* 渐变边框 */
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
border-radius: 6px;
|
||||
padding: 1px;
|
||||
background: var(--menu-active-gradient);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: transparent !important;
|
||||
opacity: 0.8;
|
||||
.n-button__content {
|
||||
background: var(--menu-active-gradient);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-action-btn-danger {
|
||||
background-color: transparent !important;
|
||||
padding: 0 0.8rem;
|
||||
color: #FF4314 !important;
|
||||
border: 1px solid #FF4314;
|
||||
border-radius: var(--border-radius-base);
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
/* 分页器样式 */
|
||||
.n-pagination {
|
||||
.n-pagination-item.n-pagination-item--active,
|
||||
.n-pagination-item.n-pagination-item--active:hover {
|
||||
position: relative;
|
||||
background-image: linear-gradient(135deg, #9C6240, #FFCF76);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.n-pagination-item.n-pagination-item--active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 6px;
|
||||
padding: 1px;
|
||||
background: linear-gradient(135deg, #9C6240, #FFCF76);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
.n-card .n-switch ,
|
||||
.n-data-table .n-switch {
|
||||
&.n-switch--active {
|
||||
.n-switch__rail {
|
||||
background: linear-gradient(135deg, #9C6240, #FFCF76) !important;
|
||||
}
|
||||
}
|
||||
.n-switch__checked {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
/* Dialog 图标 */
|
||||
.n-dialog {
|
||||
.n-dialog__title{
|
||||
color: #fff;
|
||||
}
|
||||
.n-dialog__action .n-button--primary-type .n-button__content {
|
||||
color: #000;
|
||||
}
|
||||
.n-config-provider {
|
||||
color: var(--n-text-color5);
|
||||
}
|
||||
.n-dialog__icon {
|
||||
background: linear-gradient(135deg, #9C6240, #FFCF76);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
color: transparent;
|
||||
svg,
|
||||
svg path {
|
||||
fill: url(#menu-active-icon-gradient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 下拉框 */
|
||||
.n-base-select-option {
|
||||
.n-base-select-option__check {
|
||||
svg,
|
||||
svg path {
|
||||
fill: url(#menu-active-icon-gradient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 日志样式 */
|
||||
.n-log {
|
||||
border-color: #000!important;
|
||||
background-color: #000;
|
||||
}
|
||||
/* 表格头部右边 搜索框 */
|
||||
.header-search {
|
||||
background-color: #2E2D2D!important;
|
||||
}
|
||||
/* tabs 样式 */
|
||||
.cert-main-tabs {
|
||||
> .n-tabs-nav {
|
||||
.n-tabs-tab.n-tabs-tab--active {
|
||||
position: relative;
|
||||
border-radius: var(--border-radius-base);
|
||||
background-color: transparent!important;
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
padding: 1px;
|
||||
background: linear-gradient(135deg, #9C6240, #FFCF76);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.n-tabs-bar {
|
||||
background: linear-gradient(135deg, #9C6240, #FFCF76);
|
||||
}
|
||||
.n-tabs .n-tabs-tab.n-tabs-tab--active,
|
||||
.n-tabs.n-tabs--line-type .n-tabs-tab:hover {
|
||||
color: #fff !important;
|
||||
background-color: transparent!important;
|
||||
.n-tabs-tab__label {
|
||||
font-weight: 600;
|
||||
background: var(--menu-active-gradient);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
.n-dropdown-menu {
|
||||
color: #fff;
|
||||
}
|
||||
.n-checkbox {
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
transition: background 0.3s ease, opacity 0.3s ease;
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background: var(--menu-active-gradient);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
&.n-checkbox--checked {
|
||||
&::before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.import-monitor-modal {
|
||||
.monitor-upload-progress-bar {
|
||||
background: var(--menu-active-gradient);
|
||||
}
|
||||
.n-card {
|
||||
background-color: #000;
|
||||
}
|
||||
}
|
||||
.n-layout-scroll-container {
|
||||
.n-button--tertiary-type{
|
||||
position: relative;
|
||||
background: transparent !important;
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s ease;
|
||||
.n-button__content,
|
||||
.n-button__icon {
|
||||
background: var(--menu-active-gradient);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
-webkit-text-fill-color: transparent;
|
||||
transition: opacity 1s ease;
|
||||
}
|
||||
.n-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
svg,
|
||||
svg path {
|
||||
fill: url(#menu-active-icon-gradient);
|
||||
}
|
||||
}
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 4px;
|
||||
padding: 1px;
|
||||
background: var(--menu-active-gradient);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
.n-button__border,
|
||||
.n-button__state-border{
|
||||
display: none!important;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
.n-spin .n-base-loading__container {
|
||||
svg circle {
|
||||
fill: none !important;
|
||||
stroke: url(#menu-active-icon-gradient) !important;
|
||||
stroke-width: 10px !important;
|
||||
stroke-linecap: round;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.n-loading-bar-container {
|
||||
.n-loading-bar {
|
||||
background: linear-gradient(90deg, #9c6240 0%, #ffcf76 100%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,22 @@
|
||||
--n-dialog-title-padding: 0; /* 对话框标题内边距 */
|
||||
--n-layout-content-background-color: #f8fafc; /* 内容区背景颜色 */
|
||||
|
||||
/* 自定义文本颜色 */
|
||||
--n-text-color4: #1F2937; /* 亮色主题下的文本颜色 */
|
||||
--n-text-color5: #4B5563;
|
||||
|
||||
--n-error-bg-color-light: rgba(248, 113, 113, 0.08);
|
||||
--n-error-primary-color: #FF4314;
|
||||
--n-error-text-color: #E74E4E;
|
||||
--n-error-border-color: #F56767;
|
||||
--n-success-bg-color-light: #F6FFF8;
|
||||
--n-success-status-color: #16D13B;
|
||||
--n-success-primary-color: #006A14;
|
||||
--n-success-text-color: #2D8900;
|
||||
--n-success-border-color: #25A63D;
|
||||
--n-warning-bg-color-light: rgba(255, 251, 229, 0.42);
|
||||
--n-warning-primary-color: #FF9D00;
|
||||
|
||||
/* Home View Custom Colors */
|
||||
--color-workflow-bg: rgba(16, 185, 129, 0.08);
|
||||
--color-workflow-icon-wrapper-bg: rgba(16, 185, 129, 0.15);
|
||||
@@ -21,10 +37,52 @@
|
||||
--color-monitor-text: #8B5CF6; /* 紫色,用于图标和标题 */
|
||||
|
||||
--color-decorative-element-bg: #f0f9ff; /* 用于替换 Tailwind bg-blue-50 */
|
||||
|
||||
--content-bg-base: #ffffff;
|
||||
--content-bg-secondary: #ffffff;
|
||||
|
||||
--border-color-transparent: #e5e7eb;
|
||||
--form-more-color: #18a058;
|
||||
--form-log-bg: #f9fafb;
|
||||
--form-log-code-bg: #e5e7eb;
|
||||
--form-divider-title-weight: 500;
|
||||
--monitor-upload-progress-bar-bg: #18A058;
|
||||
--table-link-type: none;
|
||||
|
||||
--setting-input-bg: #fafafa;
|
||||
--theme-select-bg: #FAFAFA;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
--n-layout-content-background-color: transparent; /* 内容区背景颜色 */
|
||||
/* 自定义文本颜色工具类 */
|
||||
.text-color4 {
|
||||
color: var(--n-text-color4);
|
||||
}
|
||||
|
||||
.text-color3 {
|
||||
color: var(--n-text-color-3);
|
||||
}
|
||||
|
||||
.text-color5 {
|
||||
color: var(--n-text-color5);
|
||||
}
|
||||
/* 暗色主题 */
|
||||
:root.dark,
|
||||
:root.defaultDark {
|
||||
--n-layout-content-background-color: #161616; /* 内容区背景颜色 */
|
||||
--n-header-color: #000;
|
||||
--n-color: #000;
|
||||
--n-sider-color: #000; /* 侧边栏背景色 */
|
||||
|
||||
/* 自定义文本颜色 - 暗色主题 */
|
||||
--n-success-bg-color-light: transparent;
|
||||
--n-success-primary-color: #fff;
|
||||
--n-text-color4: var(--n-text-color); /* 暗色主题下的浅灰色文本,与 #1F2937 形成对比 */
|
||||
--n-text-color5: #969696; /* 暗色主题下的浅灰色文本,与 #1F2937 形成对比 */
|
||||
|
||||
/* 菜单激活项渐变颜色 */
|
||||
--menu-active-gradient-start: #9C6240;
|
||||
--menu-active-gradient-end: #FFCF76;
|
||||
--menu-active-gradient: linear-gradient(135deg, var(--menu-active-gradient-start) 0%, var(--menu-active-gradient-end) 100%);
|
||||
|
||||
/* Home View Custom Colors - Dark Theme */
|
||||
--color-workflow-bg: rgba(var(--n-success-color-rgb), 0.12); /* 使用 Naive UI 暗色主题成功色的 RGB 值,并调整透明度 */
|
||||
@@ -38,5 +96,22 @@
|
||||
--color-monitor-text: #A78BFA; /* 较亮的紫色,用于暗色主题 */
|
||||
|
||||
--color-decorative-element-bg: rgba(30, 41, 59, 0.7); /* 暗色主题下的装饰背景色 */
|
||||
}
|
||||
|
||||
--border-radius-base: 6px;
|
||||
|
||||
--content-bg-base: #000000;
|
||||
|
||||
--bt-card-bg-color-active: #000000;
|
||||
--content-bg-secondary: #171717;
|
||||
|
||||
--border-color-transparent: transparent;
|
||||
--form-more-color: #ffffff;
|
||||
--form-log-bg: #2f2f2f;
|
||||
--form-log-code-bg: #000;
|
||||
--form-divider-title-weight: 600;
|
||||
--table-link-type: underline;
|
||||
--workflow-header-input-bg: #2f2f2f;
|
||||
|
||||
--setting-input-bg: #1c1c1c;
|
||||
--theme-select-bg: #171717;
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ export default defineComponent({
|
||||
<BaseComponent
|
||||
v-slots={{
|
||||
headerLeft: () => (
|
||||
<NButton type="primary" size="large" class="px-5" onClick={openAddForm}>
|
||||
<NButton type="primary" size="large" class="gradient-primary-btn px-5" onClick={openAddForm}>
|
||||
{$t('t_0_1745289355714')}
|
||||
</NButton>
|
||||
),
|
||||
headerRight: () => <SearchComponent placeholder={$t('t_0_1745289808449')} />,
|
||||
headerRight: () => <SearchComponent class="header-search" placeholder={$t('t_0_1745289808449')} />,
|
||||
content: () => (
|
||||
<div class="rounded-lg">
|
||||
<TableComponent
|
||||
|
||||
@@ -127,7 +127,7 @@ export const useController = (): AuthApiManageControllerExposes => {
|
||||
try {
|
||||
const { fetch, message } = testAccess({ id: row.id, type: row.type });
|
||||
message.value = true;
|
||||
fetch();
|
||||
await fetch();
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
}
|
||||
@@ -195,7 +195,8 @@ export const useController = (): AuthApiManageControllerExposes => {
|
||||
size="tiny"
|
||||
strong
|
||||
secondary
|
||||
type="primary"
|
||||
type="primary"
|
||||
class="table-action-btn"
|
||||
onClick={() => handleTestAccess(row)}
|
||||
>
|
||||
{$t("t_16_1746676855270")}
|
||||
@@ -205,6 +206,7 @@ export const useController = (): AuthApiManageControllerExposes => {
|
||||
strong
|
||||
secondary
|
||||
type="primary"
|
||||
class="table-action-btn"
|
||||
onClick={() => openEditForm(row)}
|
||||
>
|
||||
{$t("t_11_1745215915429")}
|
||||
@@ -214,6 +216,7 @@ export const useController = (): AuthApiManageControllerExposes => {
|
||||
strong
|
||||
secondary
|
||||
type="error"
|
||||
class="table-action-btn-danger"
|
||||
onClick={() => confirmDelete(row.id)}
|
||||
>
|
||||
{$t("t_12_1745215914312")}
|
||||
@@ -524,7 +527,6 @@ export const useApiFormController = (
|
||||
const mapTips = {
|
||||
godaddy: $t("t_1_1747984133312"),
|
||||
spaceship: "请输入 Spaceship API Secret",
|
||||
btdomain: "请输入 BTDomain Secret Key",
|
||||
};
|
||||
return callback(
|
||||
new Error(mapTips[param.value.type as keyof typeof mapTips])
|
||||
@@ -542,7 +544,7 @@ export const useApiFormController = (
|
||||
) => {
|
||||
if (!value) {
|
||||
const mapTips = {
|
||||
btdomain: "请输入 BTDomain Account ID",
|
||||
btdomain: "请输入 宝塔域名 Account ID",
|
||||
};
|
||||
return callback(
|
||||
new Error(mapTips[param.value.type as keyof typeof mapTips])
|
||||
@@ -641,7 +643,7 @@ export const useApiFormController = (
|
||||
volcengine: $t("t_3_1747365600828"),
|
||||
qiniu: $t("t_3_1747984134586"),
|
||||
doge: $t("t_0_1750320239265"),
|
||||
btdomain: "请输入 BTDomain Access Key",
|
||||
btdomain: "请输入 宝塔域名 Access Key",
|
||||
};
|
||||
return callback(
|
||||
new Error(mapTips[param.value.type as keyof typeof mapTips])
|
||||
@@ -665,7 +667,7 @@ export const useApiFormController = (
|
||||
volcengine: $t("t_4_1747365600137"),
|
||||
doge: $t("t_1_1750320241427"),
|
||||
constellix: "请输入Secret Key",
|
||||
btdomain: "请输入 BTDomain Secret Key",
|
||||
btdomain: "请输入 宝塔域名 Secret Key",
|
||||
};
|
||||
return callback(
|
||||
new Error(mapTips[param.value.type as keyof typeof mapTips])
|
||||
@@ -1188,49 +1190,49 @@ export const useApiFormController = (
|
||||
),
|
||||
useFormCustom(() => {
|
||||
return (
|
||||
<div class="mt-4 p-4 bg-gray-50 rounded-md">
|
||||
<div class="text-gray-600 space-y-4 text-lg">
|
||||
<div class="mt-4 p-4 bg-[var(--form-log-bg)] rounded-md">
|
||||
<div class="space-y-4 text-lg">
|
||||
<div>
|
||||
<div class="font-medium text-gray-700 mb-3 text-xl">
|
||||
<div class="font-medium mb-3 text-xl">
|
||||
用于部署时可用的模板变量:
|
||||
</div>
|
||||
<div class="space-y-2 ml-4">
|
||||
<div>
|
||||
<code class="px-2 py-1 bg-gray-200 rounded text-lg font-mono">
|
||||
<code class="px-2 py-1 bg-[var(--form-log-code-bg)] rounded text-lg font-mono">
|
||||
__cert__
|
||||
</code>
|
||||
:证书内容
|
||||
<span class="text-color5">:证书内容</span>
|
||||
</div>
|
||||
<div>
|
||||
<code class="px-2 py-1 bg-gray-200 rounded text-lg font-mono">
|
||||
<code class="px-2 py-1 bg-[var(--form-log-code-bg)] rounded text-lg font-mono">
|
||||
__key__
|
||||
</code>
|
||||
:私钥内容
|
||||
<span class="text-color5">:私钥内容</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-medium text-gray-700 mb-3 text-xl">
|
||||
<div class="font-medium mb-3 text-xl">
|
||||
用于申请时可用的模板变量:
|
||||
</div>
|
||||
<div class="space-y-2 ml-4">
|
||||
<div>
|
||||
<code class="px-2 py-1 bg-gray-200 rounded text-lg font-mono">
|
||||
<code class="px-2 py-1 bg-[var(--form-log-code-bg)] rounded text-lg font-mono">
|
||||
__domain__
|
||||
</code>
|
||||
:完整域名,如:_acme-challenge.allinssl.com
|
||||
<span class="text-color5">:完整域名,如:_acme-challenge.allinssl.com</span>
|
||||
</div>
|
||||
<div>
|
||||
<code class="px-2 py-1 bg-gray-200 rounded text-lg font-mono">
|
||||
<code class="px-2 py-1 bg-[var(--form-log-code-bg)] rounded text-lg font-mono">
|
||||
__keyAuth__
|
||||
</code>
|
||||
:域名解析值
|
||||
<span class="text-color5">:域名解析值</span>
|
||||
</div>
|
||||
<div>
|
||||
<code class="px-2 py-1 bg-gray-200 rounded text-lg font-mono">
|
||||
<code class="px-2 py-1 bg-[var(--form-log-code-bg)] rounded text-lg font-mono">
|
||||
__action__
|
||||
</code>
|
||||
:执行的操作,需要自行根据参数值判断,为present时执行写入,cleanup清理记录
|
||||
<span class="text-color5">:执行的操作,需要自行根据参数值判断,为present时执行写入,cleanup清理记录</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -575,7 +575,7 @@ export default defineComponent({
|
||||
trigger: () => (
|
||||
<NButton
|
||||
type={next.value ? 'primary' : 'default'}
|
||||
class={styles.footerButton}
|
||||
class={`${styles.footerButton} gradient-default-btn`}
|
||||
disabled={!param.value.provider}
|
||||
onClick={next.value ? nextStep : prevStep}
|
||||
>
|
||||
@@ -587,7 +587,7 @@ export default defineComponent({
|
||||
{next.value ? $t('t_4_1745765868807') : null}
|
||||
</NTooltip>
|
||||
{!next.value && (
|
||||
<NButton type="primary" onClick={submit}>
|
||||
<NButton class="gradient-primary-btn" type="primary" onClick={submit}>
|
||||
{$t('t_1_1745738963744')}
|
||||
</NButton>
|
||||
)}
|
||||
|
||||
@@ -378,6 +378,7 @@ export default defineComponent({
|
||||
/>
|
||||
<NButton
|
||||
type="primary"
|
||||
class="gradient-primary-btn"
|
||||
onClick={addSanInput}
|
||||
disabled={!sanInputValue.value.trim()}
|
||||
>
|
||||
|
||||
@@ -23,7 +23,7 @@ export default defineComponent({
|
||||
<BaseComponent
|
||||
v-slots={{
|
||||
headerLeft: () => (
|
||||
<NButton type="primary" onClick={handleOpenAddForm}>
|
||||
<NButton class="gradient-primary-btn" type="primary" onClick={handleOpenAddForm} >
|
||||
<PlusOutlined class="text-[var(--text-color-3)] mr-1" />
|
||||
<span>{$t('t_4_1747903685371')}</span>
|
||||
</NButton>
|
||||
|
||||
@@ -25,7 +25,7 @@ export default defineComponent({
|
||||
v-slots={{
|
||||
header: () => (
|
||||
<div class="flex items-center justify-between mb-[1.6rem]">
|
||||
<NButton type="default" onClick={() => fetch()}>
|
||||
<NButton class="gradient-default-btn" type="default" onClick={() => fetch()}>
|
||||
{$t('t_9_1746667589516')}
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
@@ -30,13 +30,13 @@ export default defineComponent({
|
||||
}}
|
||||
>
|
||||
<NCard
|
||||
class={`rounded-lg border-1 ${workflowFormData.value.templateType === item.value ? 'border-primary-500' : ''}`}
|
||||
class={`workflow-template-card rounded-lg border-1 ${workflowFormData.value.templateType === item.value ? 'border-primary-500 selected' : ''}`}
|
||||
hoverable
|
||||
>
|
||||
<NSpace align="center" justify="space-between">
|
||||
<div>
|
||||
<div class="font-medium text-[14px]">{item.label}</div>
|
||||
<div class="text-gray-500 text-[12px] mt-1">{item.description}</div>
|
||||
<div class="font-bold text-[14px]">{item.label}</div>
|
||||
<div class="text-color5 text-[12px] mt-1">{item.description}</div>
|
||||
</div>
|
||||
<NRadio checked={workflowFormData.value.templateType === item.value} />
|
||||
</NSpace>
|
||||
|
||||
@@ -56,15 +56,15 @@ export default defineComponent({
|
||||
v-slots={{
|
||||
headerLeft: () => (
|
||||
<NSpace>
|
||||
<NButton type="primary" size="large" class="px-5" onClick={handleAddWorkflow}>
|
||||
<NButton type="primary" size="large" class="px-5 gradient-primary-btn" onClick={handleAddWorkflow}>
|
||||
{$t('t_0_1747047213730')}
|
||||
</NButton>
|
||||
<NButton type="default" size="large" class="px-5" onClick={handleOpenCAManage}>
|
||||
<NButton type="default" size="large" class="px-5 gradient-default-btn" onClick={handleOpenCAManage}>
|
||||
<span class="px-2">{$t('t_0_1747903670020')}</span>
|
||||
</NButton>
|
||||
</NSpace>
|
||||
),
|
||||
headerRight: () => <SearchComponent placeholder={$t('t_1_1745227838776')} />,
|
||||
headerRight: () => <SearchComponent class="header-search" placeholder={$t('t_1_1745227838776')} />,
|
||||
content: () => (
|
||||
<div class="rounded-lg ">
|
||||
<TableComponent
|
||||
|
||||
@@ -22,61 +22,76 @@ import { router } from '@router/index'
|
||||
import { CACertificateAuthorization } from '@config/data'
|
||||
import SvgIcon from '@components/SvgIcon'
|
||||
import { isEmail } from '@baota/utils/business'
|
||||
import { useTheme } from "@baota/naive-ui/theme";
|
||||
|
||||
import type { WorkflowItem, WorkflowListParams, WorkflowHistoryParams, WorkflowHistoryItem } from '@/types/workflow'
|
||||
import type { DataTableColumn } from 'naive-ui'
|
||||
import type { TableColumn } from 'naive-ui/es/data-table/src/interface'
|
||||
import type { EabItem, EabListParams } from '@/types/access'
|
||||
import type {
|
||||
WorkflowItem,
|
||||
WorkflowListParams,
|
||||
WorkflowHistoryParams,
|
||||
WorkflowHistoryItem,
|
||||
} from "@/types/workflow";
|
||||
import type { DataTableColumn } from "naive-ui";
|
||||
import type { TableColumn } from "naive-ui/es/data-table/src/interface";
|
||||
import type { EabItem, EabListParams } from "@/types/access";
|
||||
|
||||
const {
|
||||
refreshTable,
|
||||
fetchWorkflowList,
|
||||
fetchWorkflowHistory,
|
||||
workflowFormData,
|
||||
deleteExistingWorkflow,
|
||||
executeExistingWorkflow,
|
||||
stopExistingWorkflow,
|
||||
setWorkflowActive,
|
||||
setWorkflowExecType,
|
||||
caFormData,
|
||||
fetchEabList,
|
||||
addNewEab,
|
||||
updateExistingEab,
|
||||
deleteExistingEab,
|
||||
resetCaForm,
|
||||
copyExistingWorkflow,
|
||||
} = useStore()
|
||||
const { isEdit, workDefalutNodeData, resetWorkflowData, workflowData, detectionRefresh } = useWorkflowViewStore()
|
||||
const { handleError } = useError()
|
||||
const { useFormSlot } = useFormHooks()
|
||||
refreshTable,
|
||||
fetchWorkflowList,
|
||||
fetchWorkflowHistory,
|
||||
workflowFormData,
|
||||
deleteExistingWorkflow,
|
||||
executeExistingWorkflow,
|
||||
stopExistingWorkflow,
|
||||
setWorkflowActive,
|
||||
setWorkflowExecType,
|
||||
caFormData,
|
||||
fetchEabList,
|
||||
addNewEab,
|
||||
updateExistingEab,
|
||||
deleteExistingEab,
|
||||
resetCaForm,
|
||||
copyExistingWorkflow,
|
||||
} = useStore();
|
||||
const {
|
||||
isEdit,
|
||||
workDefalutNodeData,
|
||||
resetWorkflowData,
|
||||
workflowData,
|
||||
detectionRefresh,
|
||||
} = useWorkflowViewStore();
|
||||
const { handleError } = useError();
|
||||
const { useFormSlot } = useFormHooks();
|
||||
|
||||
/**
|
||||
* @description 状态列
|
||||
* @param {string} key - 状态列的key
|
||||
* @returns {DataTableColumn<WorkflowHistoryItem>[]} 返回状态列配置数组
|
||||
*/
|
||||
const statusCol = <T extends Record<string, any>>(key: string, title: string): TableColumn<T> => ({
|
||||
title,
|
||||
key,
|
||||
width: 100,
|
||||
render: (row: T) => {
|
||||
const statusMap: Record<string, { type: string; text: string }> = {
|
||||
success: { type: 'success', text: $t('t_0_1747895713179') },
|
||||
fail: { type: 'error', text: $t('t_4_1746773348957') },
|
||||
running: { type: 'warning', text: $t('t_1_1747895712756') },
|
||||
}
|
||||
const status = statusMap[row[key] as string] || {
|
||||
type: 'default',
|
||||
text: $t('t_1_1746773348701'),
|
||||
}
|
||||
if (row[key] === 'running') refreshTable.value = true
|
||||
return (
|
||||
<NTag type={status.type as any} size="small">
|
||||
{status.text}
|
||||
</NTag>
|
||||
)
|
||||
},
|
||||
})
|
||||
const statusCol = <T extends Record<string, any>>(
|
||||
key: string,
|
||||
title: string
|
||||
): TableColumn<T> => ({
|
||||
title,
|
||||
key,
|
||||
width: 100,
|
||||
render: (row: T) => {
|
||||
const statusMap: Record<string, { type: string; text: string }> = {
|
||||
success: { type: "success", text: $t("t_0_1747895713179") },
|
||||
fail: { type: "error", text: $t("t_4_1746773348957") },
|
||||
running: { type: "warning", text: $t("t_1_1747895712756") },
|
||||
};
|
||||
const status = statusMap[row[key] as string] || {
|
||||
type: "default",
|
||||
text: $t("t_1_1746773348701"),
|
||||
};
|
||||
if (row[key] === "running") refreshTable.value = true;
|
||||
return (
|
||||
<NTag round type={status.type as any} size="small" class={status.type as any}>
|
||||
{status.text}
|
||||
</NTag>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 工作流业务逻辑控制器
|
||||
@@ -87,6 +102,8 @@ export const useController = () => {
|
||||
const route = useRoute();
|
||||
// 获取路由实例
|
||||
const router = useRouter();
|
||||
// 获取主题状态
|
||||
const { isDark } = useTheme();
|
||||
|
||||
// 判断是否为子路由
|
||||
const hasChildRoutes = computed(() => route.path !== "/auto-deploy");
|
||||
@@ -212,6 +229,7 @@ export const useController = () => {
|
||||
strong
|
||||
secondary
|
||||
type="primary"
|
||||
class="table-action-btn"
|
||||
onClick={() => handleViewHistory(row)}
|
||||
>
|
||||
{$t("t_9_1745215914666")}
|
||||
@@ -221,6 +239,7 @@ export const useController = () => {
|
||||
strong
|
||||
secondary
|
||||
type="primary"
|
||||
class="table-action-btn"
|
||||
onClick={() => handleExecuteWorkflow(row)}
|
||||
>
|
||||
{$t("t_10_1745215914342")}
|
||||
@@ -230,6 +249,7 @@ export const useController = () => {
|
||||
strong
|
||||
secondary
|
||||
type="primary"
|
||||
class="table-action-btn"
|
||||
onClick={() => handleCopyWorkflow(row)}
|
||||
>
|
||||
复制
|
||||
@@ -239,6 +259,7 @@ export const useController = () => {
|
||||
strong
|
||||
secondary
|
||||
type="primary"
|
||||
class="table-action-btn"
|
||||
onClick={() => handleEditWorkflow(row)}
|
||||
>
|
||||
{$t("t_11_1745215915429")}
|
||||
@@ -248,6 +269,7 @@ export const useController = () => {
|
||||
strong
|
||||
secondary
|
||||
type="error"
|
||||
class="table-action-btn-danger"
|
||||
onClick={() => handleDeleteWorkflow(row)}
|
||||
>
|
||||
{$t("t_12_1745215914312")}
|
||||
@@ -319,7 +341,7 @@ export const useController = () => {
|
||||
? `【${workflow.name}】 - ${$t("t_9_1745215914666")}`
|
||||
: $t("t_9_1745215914666"),
|
||||
component: HistoryModal,
|
||||
area: 850,
|
||||
area: 870,
|
||||
componentProps: { id: workflow.id.toString() },
|
||||
});
|
||||
};
|
||||
@@ -494,36 +516,36 @@ export const useController = () => {
|
||||
loading,
|
||||
param,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 添加工作流业务逻辑控制器
|
||||
* @returns {Object} 返回添加工作流业务逻辑控制器实例
|
||||
*/
|
||||
export const useAddWorkflowController = () => {
|
||||
const { confirm } = useModalHooks()
|
||||
// 表单配置
|
||||
const config = computed(() => [useFormSlot('template')])
|
||||
const { confirm } = useModalHooks();
|
||||
// 表单配置
|
||||
const config = computed(() => [useFormSlot("template")]);
|
||||
|
||||
// 表单实例
|
||||
const { component: AddWorkflowForm, data } = useForm({
|
||||
config,
|
||||
rules: {},
|
||||
defaultValue: workflowFormData,
|
||||
})
|
||||
// 表单实例
|
||||
const { component: AddWorkflowForm, data } = useForm({
|
||||
config,
|
||||
rules: {},
|
||||
defaultValue: workflowFormData,
|
||||
});
|
||||
|
||||
// 确认添加工作流
|
||||
confirm(async (close) => {
|
||||
try {
|
||||
close()
|
||||
resetWorkflowData()
|
||||
router.push(`/auto-deploy/workflow-view?type=${data.value.templateType}`)
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
}
|
||||
})
|
||||
return { AddWorkflowForm }
|
||||
}
|
||||
// 确认添加工作流
|
||||
confirm(async (close) => {
|
||||
try {
|
||||
close();
|
||||
resetWorkflowData();
|
||||
router.push(`/auto-deploy/workflow-view?type=${data.value.templateType}`);
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
}
|
||||
});
|
||||
return { AddWorkflowForm };
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 工作流历史记录业务逻辑控制器
|
||||
@@ -531,114 +553,131 @@ export const useAddWorkflowController = () => {
|
||||
* @returns {Object} 返回工作流历史记录业务逻辑控制器实例
|
||||
*/
|
||||
export const useHistoryController = (id: string) => {
|
||||
/**
|
||||
* @description 工作流历史详情
|
||||
* @param {number} workflowId - 工作流ID
|
||||
*/
|
||||
const handleViewHistoryDetail = async (workflowId: string) => {
|
||||
useModal({
|
||||
title: $t('t_0_1746579648713'),
|
||||
component: HistoryLogsModal,
|
||||
area: 730,
|
||||
componentProps: { id: workflowId },
|
||||
})
|
||||
}
|
||||
/**
|
||||
* @description 工作流历史详情
|
||||
* @param {number} workflowId - 工作流ID
|
||||
*/
|
||||
const handleViewHistoryDetail = async (workflowId: string) => {
|
||||
useModal({
|
||||
title: $t("t_0_1746579648713"),
|
||||
component: HistoryLogsModal,
|
||||
area: 730,
|
||||
componentProps: { id: workflowId },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 停止工作流执行
|
||||
* @param {WorkflowHistoryItem} historyItem - 工作流历史记录项
|
||||
*/
|
||||
const handleStopWorkflow = async (historyItem: WorkflowHistoryItem) => {
|
||||
useDialog({
|
||||
title: $t('t_0_1749204565782'),
|
||||
content: $t('t_1_1749204570473'),
|
||||
onPositiveClick: async () => {
|
||||
await stopExistingWorkflow(historyItem.id)
|
||||
await fetch() // 刷新历史记录表格
|
||||
// 触发外部主表格刷新
|
||||
refreshTable.value = true
|
||||
},
|
||||
})
|
||||
}
|
||||
/**
|
||||
* @description 停止工作流执行
|
||||
* @param {WorkflowHistoryItem} historyItem - 工作流历史记录项
|
||||
*/
|
||||
const handleStopWorkflow = async (historyItem: WorkflowHistoryItem) => {
|
||||
useDialog({
|
||||
title: $t("t_0_1749204565782"),
|
||||
content: $t("t_1_1749204570473"),
|
||||
onPositiveClick: async () => {
|
||||
await stopExistingWorkflow(historyItem.id);
|
||||
await fetch(); // 刷新历史记录表格
|
||||
// 触发外部主表格刷新
|
||||
refreshTable.value = true;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 创建历史记录表格列配置
|
||||
* @returns {DataTableColumn<WorkflowHistoryItem>[]} 返回表格列配置数组
|
||||
*/
|
||||
const createColumns = (): DataTableColumn<WorkflowHistoryItem>[] => [
|
||||
{
|
||||
title: $t('t_4_1745227838558'),
|
||||
key: 'create_time',
|
||||
width: 200,
|
||||
render: (row: WorkflowHistoryItem) => {
|
||||
// 处理数字类型的时间戳
|
||||
return row.create_time ? row.create_time : '-'
|
||||
},
|
||||
},
|
||||
{
|
||||
title: $t('t_5_1745227839906'),
|
||||
key: 'end_time',
|
||||
width: 200,
|
||||
render: (row: WorkflowHistoryItem) => {
|
||||
// 处理数字类型的时间戳
|
||||
return row.end_time ? row.end_time : '-'
|
||||
},
|
||||
},
|
||||
{
|
||||
title: $t('t_6_1745227838798'),
|
||||
key: 'exec_type',
|
||||
width: 120,
|
||||
render: (row: WorkflowHistoryItem) => (
|
||||
<NTag type={row.exec_type === 'auto' ? 'info' : 'default'} size="small" bordered={false}>
|
||||
{row.exec_type === 'auto' ? $t('t_2_1745215915397') : $t('t_3_1745215914237')}
|
||||
</NTag>
|
||||
),
|
||||
},
|
||||
statusCol<WorkflowHistoryItem>('status', $t('t_7_1745227838093')),
|
||||
{
|
||||
title: $t('t_8_1745215914610'),
|
||||
key: 'actions',
|
||||
fixed: 'right',
|
||||
align: 'right',
|
||||
width: 180,
|
||||
render: (row: WorkflowHistoryItem) => (
|
||||
<NSpace justify="end" size="small">
|
||||
{row.status === 'running' && (
|
||||
<NButton size="tiny" strong secondary type="error" onClick={() => handleStopWorkflow(row)}>
|
||||
{$t('t_0_1749204565782')}
|
||||
</NButton>
|
||||
)}
|
||||
<NButton
|
||||
size="tiny"
|
||||
strong
|
||||
secondary
|
||||
type="primary"
|
||||
onClick={() => handleViewHistoryDetail(row.id.toString())}
|
||||
>
|
||||
{$t('t_12_1745227838814')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
),
|
||||
},
|
||||
]
|
||||
/**
|
||||
* @description 创建历史记录表格列配置
|
||||
* @returns {DataTableColumn<WorkflowHistoryItem>[]} 返回表格列配置数组
|
||||
*/
|
||||
const createColumns = (): DataTableColumn<WorkflowHistoryItem>[] => [
|
||||
{
|
||||
title: $t("t_4_1745227838558"),
|
||||
key: "create_time",
|
||||
width: 200,
|
||||
render: (row: WorkflowHistoryItem) => {
|
||||
// 处理数字类型的时间戳
|
||||
return row.create_time ? row.create_time : "-";
|
||||
},
|
||||
},
|
||||
{
|
||||
title: $t("t_5_1745227839906"),
|
||||
key: "end_time",
|
||||
width: 200,
|
||||
render: (row: WorkflowHistoryItem) => {
|
||||
// 处理数字类型的时间戳
|
||||
return row.end_time ? row.end_time : "-";
|
||||
},
|
||||
},
|
||||
{
|
||||
title: $t("t_6_1745227838798"),
|
||||
key: "exec_type",
|
||||
width: 120,
|
||||
render: (row: WorkflowHistoryItem) => (
|
||||
<NTag
|
||||
type={row.exec_type === "auto" ? "info" : "default"}
|
||||
size="small"
|
||||
bordered={false}
|
||||
class={row.exec_type === "auto" ? "tag-info" : "tag-default"}
|
||||
>
|
||||
{row.exec_type === "auto"
|
||||
? $t("t_2_1745215915397")
|
||||
: $t("t_3_1745215914237")}
|
||||
</NTag>
|
||||
),
|
||||
},
|
||||
statusCol<WorkflowHistoryItem>("status", $t("t_7_1745227838093")),
|
||||
{
|
||||
title: $t("t_8_1745215914610"),
|
||||
key: "actions",
|
||||
fixed: "right",
|
||||
align: "right",
|
||||
width: 180,
|
||||
render: (row: WorkflowHistoryItem) => (
|
||||
<NSpace justify="end" size="small">
|
||||
{row.status === "running" && (
|
||||
<NButton
|
||||
size="tiny"
|
||||
strong
|
||||
secondary
|
||||
type="error"
|
||||
onClick={() => handleStopWorkflow(row)}
|
||||
>
|
||||
{$t("t_0_1749204565782")}
|
||||
</NButton>
|
||||
)}
|
||||
<NButton
|
||||
size="tiny"
|
||||
strong
|
||||
secondary
|
||||
type="primary"
|
||||
class="table-action-btn"
|
||||
onClick={() => handleViewHistoryDetail(row.id.toString())}
|
||||
>
|
||||
{$t("t_12_1745227838814")}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
// 表格实例
|
||||
const { TableComponent, PageComponent, loading, fetch } = useTable<WorkflowHistoryItem, WorkflowHistoryParams>({
|
||||
config: createColumns(),
|
||||
request: fetchWorkflowHistory,
|
||||
defaultValue: { id, p: 1, limit: 10 },
|
||||
alias: { page: 'p', pageSize: 'limit' },
|
||||
watchValue: ['p', 'limit'],
|
||||
storage: 'autoDeployHistoryPageSize',
|
||||
})
|
||||
// 表格实例
|
||||
const { TableComponent, PageComponent, loading, fetch } = useTable<
|
||||
WorkflowHistoryItem,
|
||||
WorkflowHistoryParams
|
||||
>({
|
||||
config: createColumns(),
|
||||
request: fetchWorkflowHistory,
|
||||
defaultValue: { id, p: 1, limit: 10 },
|
||||
alias: { page: "p", pageSize: "limit" },
|
||||
watchValue: ["p", "limit"],
|
||||
storage: "autoDeployHistoryPageSize",
|
||||
});
|
||||
|
||||
return {
|
||||
TableComponent,
|
||||
PageComponent,
|
||||
loading,
|
||||
fetch,
|
||||
}
|
||||
}
|
||||
return {
|
||||
TableComponent,
|
||||
PageComponent,
|
||||
loading,
|
||||
fetch,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 处理CA授权类型
|
||||
@@ -699,10 +738,10 @@ export const useCAManageController = (props: { type: string }) => {
|
||||
fixed: 'right' as const,
|
||||
render: (row: EabItem) => (
|
||||
<NSpace justify="end">
|
||||
<NButton size="tiny" strong secondary type="primary" onClick={() => handleEdit(row)}>
|
||||
<NButton class="table-action-btn" size="tiny" strong secondary type="primary" onClick={() => handleEdit(row)}>
|
||||
{$t('t_11_1745215915429')}
|
||||
</NButton>
|
||||
<NButton size="tiny" strong secondary type="error" onClick={() => confirmDelete(row.id.toString())}>
|
||||
<NButton class="table-action-btn-danger" size="tiny" strong secondary type="error" onClick={() => confirmDelete(row.id.toString())}>
|
||||
{$t('t_12_1745215914312')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { NButton, NImage, NBadge } from 'naive-ui'
|
||||
import { useTheme } from "@baota/naive-ui/theme";
|
||||
import { $t } from '@locales/index'
|
||||
interface FreeProductCardProps {
|
||||
product: {
|
||||
@@ -32,6 +33,8 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
// 获取主题状态
|
||||
const { isDark } = useTheme();
|
||||
// 判断是否为通配符证书
|
||||
const isWildcard = computed(() => {
|
||||
return props.product.title.toLowerCase().includes($t('t_10_1746667589575'))
|
||||
@@ -54,7 +57,7 @@ export default defineComponent({
|
||||
sectigo: '/static/icons/sectigo-ico.png',
|
||||
positive: '/static/icons/positive-ico.png',
|
||||
ssltrus: '/static/icons/ssltrus-ico.png',
|
||||
"let's encrypt": '/static/icons/letsencrypt-icon.svg',
|
||||
"let's encrypt": isDark.value ? '/static/icons/letsencrypt-icon-dark.svg' : '/static/icons/letsencrypt-icon.svg',
|
||||
}
|
||||
return Object.keys(brandIconMap).find((key) => brandLower.includes(key))
|
||||
? brandIconMap[Object.keys(brandIconMap).find((key) => brandLower.includes(key)) as string]
|
||||
@@ -62,14 +65,14 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div class="relative border border-gray-200 rounded-[0.8rem] p-[2rem] transition-all duration-300 h-full flex flex-col bg-white shadow-sm hover:shadow-md hover:border-blue-100 hover:-translate-y-[0.2rem]">
|
||||
<div class="bg-[var(--content-bg-secondary)] relative border border-[var(--border-color-transparent)] rounded-[0.8rem] p-[2rem] transition-all duration-300 h-full flex flex-col shadow-sm hover:shadow-md hover:-translate-y-[0.2rem]">
|
||||
{props.product.brand === "Let's Encrypt" && (
|
||||
<div class="absolute top-[1.2rem] right-[1.2rem] z-10">
|
||||
<NBadge type="info" value={$t('t_12_1746667589733')} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="flex flex-col items-center text-center mb-[2rem] pb-[1.6rem] border-b border-gray-100">
|
||||
<div class="flex flex-col items-center text-center mb-[2rem] pb-[1.6rem] border-b border-[var(--n-tab-border-color)]">
|
||||
<div class="flex-none h-[6rem] w-2/5 mb-[1.2rem] flex items-center justify-center">
|
||||
<NImage
|
||||
src={getBrandIcon(props.product.brand)}
|
||||
@@ -78,8 +81,8 @@ export default defineComponent({
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-1 w-full">
|
||||
<h3 class="font-semibold mb-[0.8rem] text-gray-800 leading-tight">{props.product.title}</h3>
|
||||
<p class="text-[1.3rem] text-gray-500 m-0 leading-relaxed px-[0.8rem]">
|
||||
<h3 class="font-semibold mb-[0.8rem] leading-tight">{props.product.title}</h3>
|
||||
<p class="text-[1.3rem] text-color5 m-0 leading-relaxed px-[0.8rem]">
|
||||
{props.product.brand + $t('t_13_1746667599218')}
|
||||
</p>
|
||||
</div>
|
||||
@@ -88,24 +91,24 @@ export default defineComponent({
|
||||
<div class="flex-1 flex flex-col mt-0">
|
||||
<div class="text-[1.3rem] mb-[2.4rem] flex-1 text-left">
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="font-medium text-gray-500 flex-none w-[9rem]">{$t('t_14_1746667590827')}</span>
|
||||
<span class="flex-1 text-gray-700">{props.product.num + $t('t_15_1746667588493')}</span>
|
||||
<span class="font-medium text-color5 flex-none w-[9rem]">{$t('t_14_1746667590827')}</span>
|
||||
<span class="flex-1">{props.product.num + $t('t_15_1746667588493')}</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="font-medium text-gray-500 flex-none w-[9rem]">{$t('t_16_1746667591069')}</span>
|
||||
<span class="flex-1 text-gray-700">{$t('t_17_1746667588785')}</span>
|
||||
<span class="font-medium text-color5 flex-none w-[9rem]">{$t('t_16_1746667591069')}</span>
|
||||
<span class="flex-1">{$t('t_17_1746667588785')}</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="font-medium text-gray-500 flex-none w-[9rem]">{$t('t_19_1746667589295')}</span>
|
||||
<span class="flex-1 text-gray-700">{props.product.valid_days + $t('t_20_1746667588453')}</span>
|
||||
<span class="font-medium text-color5 flex-none w-[9rem]">{$t('t_19_1746667589295')}</span>
|
||||
<span class="flex-1">{props.product.valid_days + $t('t_20_1746667588453')}</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="font-medium text-gray-500 flex-none w-[9rem]">{$t('t_21_1746667590834')}</span>
|
||||
<span class="flex-1 text-gray-700">{$t('t_17_1746667588785')}</span>
|
||||
<span class="font-medium text-color5 flex-none w-[9rem]">{$t('t_21_1746667590834')}</span>
|
||||
<span class="flex-1">{$t('t_17_1746667588785')}</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed whitespace-nowrap overflow-hidden text-ellipsis text-gray-500">
|
||||
<span class="font-medium text-gray-500 flex-none w-[9rem]">{$t('t_22_1746667591024')}</span>
|
||||
<span class="flex-1 text-gray-600 whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
<div class="flex mb-[1rem] leading-relaxed whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
<span class="font-medium text-color5 flex-none w-[9rem]">{$t('t_22_1746667591024')}</span>
|
||||
<span class="flex-1 whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{isWildcard.value
|
||||
? isMultiDomain.value
|
||||
? $t('t_23_1746667591989')
|
||||
@@ -117,15 +120,15 @@ export default defineComponent({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center mt-[1.6rem] pt-[1.6rem] border-t border-gray-100">
|
||||
<div class="flex justify-between items-center mt-[1.6rem] pt-[1.6rem] border-t border-[var(--n-tab-border-color)]">
|
||||
<div class="flex-1 flex flex-col">
|
||||
<div class="flex items-baseline justify-start">
|
||||
<span class="text-[2.2rem] font-bold text-green-500 leading-tight">{$t('t_27_1746667589350')}</span>
|
||||
<span class="gradient-primary-txt text-[2.2rem] font-bold text-green-500 leading-tight">{$t('t_27_1746667589350')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<NButton
|
||||
type="primary"
|
||||
class="flex-none transition-all duration-300 min-w-[9rem] hover:scale-105 hover:shadow-md"
|
||||
class="gradient-primary-btn flex-none transition-all duration-300 min-w-[9rem] hover:scale-105 hover:shadow-md"
|
||||
onClick={handleApply}
|
||||
strong
|
||||
round
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import { NButton, NCard, NTag, NImage, NBadge, NList, NListItem, NTooltip } from 'naive-ui'
|
||||
import { useTheme } from "@baota/naive-ui/theme";
|
||||
|
||||
interface ProductCardProps {
|
||||
product: {
|
||||
pid: number
|
||||
brand: string
|
||||
type: string
|
||||
title: string
|
||||
add_price: number
|
||||
other_price: number
|
||||
num: number
|
||||
price: number
|
||||
discount: number
|
||||
ipssl?: number
|
||||
state: number
|
||||
install_price: number
|
||||
src_price: number
|
||||
code: string
|
||||
}
|
||||
formatPrice: (price: number) => string
|
||||
onBuy: (id: number) => void
|
||||
product: {
|
||||
pid: number;
|
||||
brand: string;
|
||||
type: string;
|
||||
title: string;
|
||||
add_price: number;
|
||||
other_price: number;
|
||||
num: number;
|
||||
price: number;
|
||||
discount: number;
|
||||
ipssl?: number;
|
||||
state: number;
|
||||
install_price: number;
|
||||
src_price: number;
|
||||
code: string;
|
||||
};
|
||||
formatPrice: (price: number) => string;
|
||||
onBuy: (id: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,133 +29,161 @@ interface ProductCardProps {
|
||||
* @param onBuy - 购买按钮点击处理函数
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'ProductCard',
|
||||
props: {
|
||||
product: {
|
||||
type: Object as PropType<ProductCardProps['product']>,
|
||||
required: true,
|
||||
},
|
||||
formatPrice: {
|
||||
type: Function as PropType<ProductCardProps['formatPrice']>,
|
||||
required: true,
|
||||
},
|
||||
onBuy: {
|
||||
type: Function as PropType<ProductCardProps['onBuy']>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
// 判断是否为通配符证书
|
||||
const isWildcard = computed(() => {
|
||||
return props.product.title.toLowerCase().includes('通配符')
|
||||
})
|
||||
name: "ProductCard",
|
||||
props: {
|
||||
product: {
|
||||
type: Object as PropType<ProductCardProps["product"]>,
|
||||
required: true,
|
||||
},
|
||||
formatPrice: {
|
||||
type: Function as PropType<ProductCardProps["formatPrice"]>,
|
||||
required: true,
|
||||
},
|
||||
onBuy: {
|
||||
type: Function as PropType<ProductCardProps["onBuy"]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
// 获取主题状态
|
||||
const { isDark } = useTheme();
|
||||
|
||||
// 判断是否为多域名证书
|
||||
const isMultiDomain = computed(() => {
|
||||
return props.product.title.toLowerCase().includes('多域名')
|
||||
})
|
||||
// 判断是否为通配符证书
|
||||
const isWildcard = computed(() => {
|
||||
return props.product.title.toLowerCase().includes("通配符");
|
||||
});
|
||||
|
||||
// 处理购买按钮点击
|
||||
const handleBuy = () => {
|
||||
props.onBuy(props.product.pid)
|
||||
}
|
||||
// 判断是否为多域名证书
|
||||
const isMultiDomain = computed(() => {
|
||||
return props.product.title.toLowerCase().includes("多域名");
|
||||
});
|
||||
|
||||
// 获取品牌图标
|
||||
const getBrandIcon = (brand: string) => {
|
||||
const brandLower = brand.toLowerCase()
|
||||
if (brandLower.includes('sectigo')) return '/static/icons/sectigo-ico.png'
|
||||
if (brandLower.includes('positive')) return '/static/icons/positive-ico.png'
|
||||
if (brandLower.includes('锐安信')) return '/static/icons/ssltrus-ico.png'
|
||||
if (brandLower.includes("let's encrypt")) return '/static/icons/letsencrypt-icon.svg'
|
||||
if (brandLower.includes('宝塔证书')) return '/static/icons/btssl.svg'
|
||||
}
|
||||
// 处理购买按钮点击
|
||||
const handleBuy = () => {
|
||||
props.onBuy(props.product.pid);
|
||||
};
|
||||
|
||||
return () => (
|
||||
<div class="relative border border-gray-200 rounded-[0.8rem] p-[2rem] transition-all duration-300 h-full flex flex-col bg-white shadow-sm hover:shadow-md hover:border-blue-100 hover:-translate-y-[0.2rem]">
|
||||
{props.product.discount < 1 && (
|
||||
<div class="absolute top-[1.2rem] right-[1.2rem] z-10">
|
||||
<NBadge type="success" value="推荐" />
|
||||
</div>
|
||||
)}
|
||||
// 获取品牌图标(根据主题切换)
|
||||
const getBrandIcon = (brand: string) => {
|
||||
const brandLower = brand.toLowerCase();
|
||||
if (brandLower.includes("sectigo")) {
|
||||
return isDark.value
|
||||
? "/static/icons/sectigo-ico-dark.svg"
|
||||
: "/static/icons/sectigo-ico.svg";
|
||||
}
|
||||
if (brandLower.includes("positive"))
|
||||
return "/static/icons/positive-ico.png";
|
||||
if (brandLower.includes("锐安信")) {
|
||||
return isDark.value
|
||||
? "/static/icons/ssltrus-ico-dark.png"
|
||||
: "/static/icons/ssltrus-ico.png";
|
||||
}
|
||||
if (brandLower.includes("let's encrypt"))
|
||||
return "/static/icons/letsencrypt-icon.svg";
|
||||
if (brandLower.includes("宝塔证书")) return "/static/icons/btssl.svg";
|
||||
};
|
||||
|
||||
<div class="flex flex-col items-center text-center mb-[2rem] pb-[1.6rem] border-b border-gray-100">
|
||||
<div class="flex-none h-[6rem] w-2/5 mb-[1.2rem] flex items-center justify-center">
|
||||
<NImage
|
||||
width="100%"
|
||||
src={getBrandIcon(props.product.brand)}
|
||||
fallbackSrc="/static/icons/default.png"
|
||||
alt={props.product.brand}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-1 w-full">
|
||||
<h3 class="font-semibold mb-[0.8rem] text-gray-800 leading-tight">{props.product.title}</h3>
|
||||
<p class="text-[1.3rem] text-gray-500 m-0 leading-relaxed px-[0.8rem]">
|
||||
{props.product.brand === '宝塔证书'
|
||||
? '宝塔证书是新国产证书品牌,支持 ECC、RSA 及我国商用密码 SM2 等标准算法,兼容国密浏览器'
|
||||
: `${props.product.brand}是知名的证书颁发机构,提供高质量的SSL证书解决方案`}
|
||||
。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
return () => (
|
||||
<div class="bg-[var(--content-bg-secondary)] relative border border-[var(--border-color-transparent)] rounded-[0.8rem] p-[2rem] transition-all duration-300 h-full flex flex-col shadow-sm hover:shadow-md hover:-translate-y-[0.2rem]">
|
||||
{props.product.discount < 1 && (
|
||||
<div class="absolute top-[1.2rem] right-[1.2rem] z-10">
|
||||
<NBadge type="success" value="推荐" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="flex-1 flex flex-col mt-0">
|
||||
<div class="text-[1.3rem] mb-[2.4rem] flex-1 text-left">
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="font-medium text-gray-500 flex-none w-[9rem]">支持域名数:</span>
|
||||
<span class="flex-1 text-gray-700">{props.product.num}个</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="font-medium text-gray-500 flex-none w-[9rem]">支持通配符:</span>
|
||||
<span class="flex-1 text-gray-700">{isWildcard.value ? '支持' : '不支持'}</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="font-medium text-gray-500 flex-none w-[9rem]">绿色地址栏:</span>
|
||||
<span class="flex-1 text-gray-700">{props.product.type.includes('EV') ? '显示' : '不显示'}</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="font-medium text-gray-500 flex-none w-[9rem]">支持小程序:</span>
|
||||
<span class="flex-1 text-gray-700">支持</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed whitespace-nowrap overflow-hidden text-ellipsis text-gray-500">
|
||||
<span class="font-medium text-gray-500 flex-none w-[9rem]">适用网站:</span>
|
||||
<span class="flex-1 text-gray-600 whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{props.product?.ipssl
|
||||
? '支持IP SSL证书'
|
||||
: isWildcard.value
|
||||
? isMultiDomain.value
|
||||
? '*.bt.cn、*.btnode.cn'
|
||||
: '*.bt.cn'
|
||||
: isMultiDomain.value
|
||||
? 'bt.cn、btnode.cn'
|
||||
: 'www.bt.cn、bt.cn'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center text-center mb-[2rem] pb-[1.6rem] border-b border-[var(--n-tab-border-color)]">
|
||||
<div class="flex-none h-[6rem] w-2/5 mb-[1.2rem] flex items-center justify-center">
|
||||
<NImage
|
||||
width="100%"
|
||||
src={getBrandIcon(props.product.brand)}
|
||||
fallbackSrc="/static/icons/default.png"
|
||||
alt={props.product.brand}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-1 w-full">
|
||||
<h3 class="font-semibold mb-[0.8rem] leading-tight">
|
||||
{props.product.title}
|
||||
</h3>
|
||||
<p class="text-[1.3rem] text-gray-500 m-0 leading-relaxed px-[0.8rem] text-color5">
|
||||
{props.product.brand === "宝塔证书"
|
||||
? "宝塔证书是新国产证书品牌,支持 ECC、RSA 及我国商用密码 SM2 等标准算法,兼容国密浏览器"
|
||||
: `${props.product.brand}是知名的证书颁发机构,提供高质量的SSL证书解决方案`}
|
||||
。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col mt-0">
|
||||
<div class="text-[1.3rem] mb-[2.4rem] flex-1 text-left">
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="text-color5 font-medium text-gray-500 flex-none w-[9rem]">
|
||||
支持域名数:
|
||||
</span>
|
||||
<span class="flex-1">{props.product.num}个</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="text-color5 font-medium text-gray-500 flex-none w-[9rem]">
|
||||
支持通配符:
|
||||
</span>
|
||||
<span class="flex-1">{isWildcard.value ? "支持" : "不支持"}</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="text-color5 font-medium text-gray-500 flex-none w-[9rem]">
|
||||
绿色地址栏:
|
||||
</span>
|
||||
<span class="flex-1">
|
||||
{props.product.type.includes("EV") ? "显示" : "不显示"}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed">
|
||||
<span class="text-color5 font-medium text-gray-500 flex-none w-[9rem]">
|
||||
支持小程序:
|
||||
</span>
|
||||
<span class="flex-1">支持</span>
|
||||
</div>
|
||||
<div class="flex mb-[1rem] leading-relaxed whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
<span class="text-color5 font-medium text-gray-500 flex-none w-[9rem]">
|
||||
适用网站:
|
||||
</span>
|
||||
<span class="flex-1 whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{props.product?.ipssl
|
||||
? "支持IP SSL证书"
|
||||
: isWildcard.value
|
||||
? isMultiDomain.value
|
||||
? "*.bt.cn、*.btnode.cn"
|
||||
: "*.bt.cn"
|
||||
: isMultiDomain.value
|
||||
? "bt.cn、btnode.cn"
|
||||
: "www.bt.cn、bt.cn"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center mt-[1.6rem] pt-[1.6rem] border-t border-gray-100">
|
||||
<div class="flex-1 flex flex-col">
|
||||
<div class="flex items-baseline justify-start">
|
||||
<span class="text-[2.2rem] font-bold text-red-500 leading-tight">
|
||||
{props.formatPrice(props.product.price)}
|
||||
</span>
|
||||
<span class="text-[1.3rem] text-gray-400 ml-[0.4rem]">元/年</span>
|
||||
</div>
|
||||
<div class="text-[1.3rem] text-gray-400 line-through mt-[0.4rem]">
|
||||
原价 {props.formatPrice(props.product.other_price)}元/年
|
||||
</div>
|
||||
</div>
|
||||
<NButton
|
||||
type="primary"
|
||||
class="flex-none transition-all duration-300 min-w-[9rem] hover:scale-105 hover:shadow-md"
|
||||
onClick={handleBuy}
|
||||
strong
|
||||
round
|
||||
>
|
||||
立即查看
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
<div class="flex justify-between items-center mt-[1.6rem] pt-[1.6rem] border-t border-[var(--n-tab-border-color)]">
|
||||
<div class="flex-1 flex flex-col">
|
||||
<div class="flex items-baseline justify-start">
|
||||
<span class="text-[2.2rem] font-bold text-red-500 leading-tight">
|
||||
{props.formatPrice(props.product.price)}
|
||||
</span>
|
||||
<span class="text-[1.3rem] text-[#969696] ml-[0.4rem]">
|
||||
元/年
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-[1.3rem] text-[#969696] line-through mt-[0.4rem]">
|
||||
原价 {props.formatPrice(props.product.other_price)}元/年
|
||||
</div>
|
||||
</div>
|
||||
<NButton
|
||||
type="primary"
|
||||
class="gradient-primary-btn flex-none transition-all duration-300 min-w-[9rem] hover:scale-105 hover:shadow-md"
|
||||
onClick={handleBuy}
|
||||
strong
|
||||
round
|
||||
>
|
||||
立即查看
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -26,22 +26,21 @@ export default defineComponent({
|
||||
|
||||
return () => (
|
||||
<div class="w-full max-w-[160rem] mx-auto p-[2rem]">
|
||||
<div class="bg-white rounded-[0.6rem] p-[2.4rem] mb-[3rem]">
|
||||
<div class="bg-[var(--content-bg-base)] rounded-[0.6rem] p-[2.4rem] mb-[3rem]">
|
||||
{/* 主标签页:商业证书/免费证书 */}
|
||||
<NTabs
|
||||
class="rounded-[1.2rem] p-[0.6rem]"
|
||||
class="cert-main-tabs rounded-[1.2rem] p-[0.6rem]"
|
||||
type="segment"
|
||||
v-model:value={activeMainTab.value}
|
||||
size="large"
|
||||
justifyContent="space-evenly"
|
||||
>
|
||||
{mainTabOptions.value.map((tab) => (
|
||||
<NTabPane key={tab.key} name={tab.key}>
|
||||
{mainTabOptions.value.map((tab, index) => (
|
||||
<NTabPane key={tab.key} name={tab.key} class={`${index === 0 ? 'main-tabs' : ''}`}>
|
||||
{{
|
||||
tab: () => (
|
||||
<div class="flex items-center my-[1rem] px-[0.8rem] py-[0.4rem] rounded-[0.8rem] transition-all duration-300 hover:bg-black/5 ">
|
||||
<NIcon size="20">{tab.key === 'commercial' ? <ShoppingCartOutlined /> : <LockOutlined />}</NIcon>
|
||||
<span class="ml-[0.8rem]">{tab.title}</span>
|
||||
<span class="ml-[0.8rem] font-semibold">{tab.title}</span>
|
||||
</div>
|
||||
),
|
||||
default: () => (
|
||||
|
||||
@@ -52,7 +52,7 @@ export const useController = () => {
|
||||
const handleOpenApplyModal = () => {
|
||||
useModal({
|
||||
title: $t(`申请免费证书 - Let's Encrypt`),
|
||||
area: '500px',
|
||||
area: '520px',
|
||||
component: CertificateForm,
|
||||
footer: true,
|
||||
})
|
||||
|
||||
@@ -23,11 +23,11 @@ export default defineComponent({
|
||||
<BaseComponent
|
||||
v-slots={{
|
||||
headerLeft: () => (
|
||||
<NButton type="primary" size="large" class="px-5" onClick={openUploadModal}>
|
||||
<NButton class="gradient-primary-btn px-5" type="primary" size="large" onClick={openUploadModal}>
|
||||
{$t('t_13_1745227838275')}
|
||||
</NButton>
|
||||
),
|
||||
headerRight: () => <SearchComponent placeholder={$t('t_14_1745227840904')} />,
|
||||
headerRight: () => <SearchComponent class="header-search" placeholder={$t('t_0_1763542847861')} />,
|
||||
content: () => (
|
||||
<div class="rounded-lg">
|
||||
<TableComponent
|
||||
|
||||
@@ -101,10 +101,10 @@ export const useController = () => {
|
||||
// 如果无法计算剩余天数,显示获取失败
|
||||
if (endDay === null) {
|
||||
return (
|
||||
<NTag type="error" size="small">
|
||||
获取失败
|
||||
<NTag round type="error" size="small">
|
||||
获取失败
|
||||
</NTag>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 根据剩余天数确定显示样式和文本
|
||||
@@ -118,7 +118,7 @@ export const useController = () => {
|
||||
const [, type, text] = matchedConfig ?? ['default', 'error', '获取失败']
|
||||
|
||||
return (
|
||||
<NTag type={type} size="small">
|
||||
<NTag round type={type} size="small">
|
||||
{text}
|
||||
</NTag>
|
||||
)
|
||||
@@ -143,13 +143,13 @@ export const useController = () => {
|
||||
width: 200,
|
||||
render: (row: CertItem) => (
|
||||
<NSpace justify="end">
|
||||
<NButton size="tiny" strong secondary type="primary" onClick={() => openViewModal(row)}>
|
||||
<NButton size="tiny" strong secondary type="primary" class="table-action-btn" onClick={() => openViewModal(row)}>
|
||||
查看
|
||||
</NButton>
|
||||
<NButton size="tiny" strong secondary type="primary" onClick={() => downloadExistingCert(row.id.toString())}>
|
||||
<NButton size="tiny" strong secondary type="primary" class="table-action-btn" onClick={() => downloadExistingCert(row.id.toString())}>
|
||||
{$t('t_25_1745227838080')}
|
||||
</NButton>
|
||||
<NButton size="tiny" strong secondary type="error" onClick={() => handleDeleteCert(row)}>
|
||||
<NButton size="tiny" strong secondary type="error" class="table-action-btn-danger" onClick={() => handleDeleteCert(row)}>
|
||||
{$t('t_12_1745215914312')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
|
||||
@@ -18,15 +18,29 @@
|
||||
|
||||
/* 快捷入口卡片基础样式 */
|
||||
.quickEntryCard {
|
||||
height: 100% ;
|
||||
height: 100%;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 0.6rem; /* 圆角 */
|
||||
border-radius: 1rem; /* 圆角 */
|
||||
background-color: transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.quickEntryCard:hover {
|
||||
transform: translateY(-4px); /* 悬停时上移效果更明显 */
|
||||
}
|
||||
|
||||
.quickEntryImage {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.quickEntryCardContent {
|
||||
padding: 24px;
|
||||
flex: 1;
|
||||
background-color: var(--n-body-color);
|
||||
width: 100%;
|
||||
padding: 24px;
|
||||
}
|
||||
/* 工作流快捷入口特定样式 */
|
||||
.workflow {
|
||||
background: var(--color-workflow-bg); /* 使用 variable.css 中定义的变量 */
|
||||
@@ -81,18 +95,29 @@
|
||||
/* 快捷入口卡片内标题样式 */
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
font-weight: 500; /* 中等字重 */
|
||||
font-weight: 600; /* 中等字重 */
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
/* 表格内文本、卡片内描述文本等通用文本样式 */
|
||||
.tableText {
|
||||
font-size: 1.4rem; /* 14px */
|
||||
color: var(--n-text-color2); /* Naive UI secondary text color */
|
||||
line-height: 1.6; /* Improved readability */
|
||||
}
|
||||
|
||||
.tableTitle {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
color: var(--n-text-color4);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.tableSubtitle {
|
||||
font-size: 1.2rem;
|
||||
color: var(--n-text-color-3);
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
/* 概览卡片中的图标区域样式 */
|
||||
.workflowIcon,
|
||||
@@ -117,3 +142,30 @@
|
||||
.bgUtilDecorative {
|
||||
background-color: var(--n-primary-color); /* 修改:使用 Naive UI 标准变量 (假设 primaryColor 是期望的装饰色) */
|
||||
}
|
||||
|
||||
.borderColor {
|
||||
border-color: #E5E7EB;
|
||||
}
|
||||
|
||||
:global(.defaultDark) {
|
||||
.quickEntryImageWrapper {
|
||||
background-color: var(--n-body-color);
|
||||
padding: 4px;
|
||||
img {
|
||||
border-top-left-radius: 1rem;
|
||||
border-top-right-radius: 1rem;
|
||||
}
|
||||
}
|
||||
.gradientNumber {
|
||||
background-image: linear-gradient(90deg, #9C6240, #FFCF76);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
.tableSubtitle {
|
||||
color: #856C5F;
|
||||
}
|
||||
.borderColor {
|
||||
border-color: #454545;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,28 @@
|
||||
import { defineComponent } from 'vue'; // 修改:移除 computed
|
||||
import { NCard, NSpin, NIcon, NEmpty, NDataTable, NButton } from 'naive-ui';
|
||||
import { CloudMonitoring, Flow, ArrowRight } from '@vicons/carbon';
|
||||
import { Certificate20Regular } from '@vicons/fluent';
|
||||
import { useThemeCssVar } from '@baota/naive-ui/theme';
|
||||
import { defineComponent, computed } from "vue";
|
||||
import {
|
||||
NCard,
|
||||
NSpin,
|
||||
NIcon,
|
||||
NEmpty,
|
||||
NDataTable,
|
||||
NButton,
|
||||
NImage,
|
||||
} from "naive-ui";
|
||||
import { CloudMonitoring, Flow, ArrowRight } from "@vicons/carbon";
|
||||
import { Certificate20Regular } from "@vicons/fluent";
|
||||
import { useThemeCssVar, useTheme } from "@baota/naive-ui/theme";
|
||||
|
||||
// Absolute Internal Imports - Utilities
|
||||
import { $t } from '@locales/index';
|
||||
import { $t } from "@locales/index";
|
||||
|
||||
// Relative Internal Imports
|
||||
import { useController } from './useController';
|
||||
import { useStore } from './useStore';
|
||||
import { useController } from "./useController";
|
||||
import { useStore } from "./useStore";
|
||||
|
||||
// Side-effect Imports
|
||||
import styles from './index.module.css';
|
||||
import styles from "./index.module.css";
|
||||
|
||||
// 资源导入/主题切换逻辑已迁移至 useController
|
||||
|
||||
/**
|
||||
* @component HomeView
|
||||
@@ -20,239 +30,377 @@ import styles from './index.module.css';
|
||||
* 负责展示应用概览信息、工作流历史以及快捷入口。
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'HomeView',
|
||||
setup() {
|
||||
const { loading } = useStore()
|
||||
const { overviewData, pushToWorkflow, pushToCert, pushToMonitor, pushToCertManage, createColumns } = useController()
|
||||
const columns = createColumns()
|
||||
name: "HomeView",
|
||||
setup() {
|
||||
const { loading } = useStore();
|
||||
const {
|
||||
overviewData,
|
||||
pushToWorkflow,
|
||||
pushToCert,
|
||||
pushToMonitor,
|
||||
pushToCertManage,
|
||||
createColumns,
|
||||
statusIcons,
|
||||
quickEntryImgs,
|
||||
} = useController();
|
||||
const columns = createColumns();
|
||||
|
||||
// 参考 layout/index.tsx 的用法,直接获取需要的 Naive UI 主题变量
|
||||
// useThemeCssVar 会将这些 camelCase 变量名转换为 kebab-case CSS 变量 (e.g., successColor -> --n-success-color)
|
||||
// 并将它们应用到绑定 style 的元素上。
|
||||
const cssVars = useThemeCssVar(['successColor', 'errorColor', 'warningColor', 'primaryColor']);
|
||||
// 获取主题状态
|
||||
const { isDark } = useTheme();
|
||||
// useThemeCssVar 会将这些 camelCase 变量名转换为 kebab-case CSS 变量 (e.g., successColor -> --n-success-color)
|
||||
// 并将它们应用到绑定 style 的元素上。
|
||||
const cssVars = useThemeCssVar([
|
||||
"successColor",
|
||||
"errorColor",
|
||||
"warningColor",
|
||||
"primaryColor",
|
||||
]);
|
||||
|
||||
return () => (
|
||||
<div class="mx-auto max-w-[1600px] w-full p-6" style={cssVars.value}>
|
||||
<NSpin show={loading.value}>
|
||||
<div class="flex flex-col h-full gap-8">
|
||||
{/* 概览模块 */}
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{/* 自动化工作流概览卡片 */}
|
||||
<div onClick={() => pushToWorkflow()} class="cursor-pointer relative">
|
||||
<div
|
||||
class={`absolute right-0 top-0 w-24 h-24 rounded-full opacity-70 -z-10 ${styles.bgUtilDecorative}`}
|
||||
></div>
|
||||
<NCard class="transition-all duration-300 rounded-[0.6rem]" hoverable={true} bordered={false}>
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="flex-1">
|
||||
<div class={styles.tableText}>{$t('t_2_1746773350970')}</div>
|
||||
<div class="flex items-center xl:space-x-5 lg:space-x-4 md:space-x-3 space-x-3">
|
||||
<div>
|
||||
<span class="xl:text-[2.4rem] lg:text-[2.2rem] md:text-[2rem] text-[1.8rem] font-bold">
|
||||
{overviewData.value.workflow.count}
|
||||
</span>
|
||||
<p class={styles.tableText}>{$t('t_3_1746773348798')}</p>
|
||||
</div>
|
||||
<div class="border-l-2 xl:pl-[2rem] xl:ml-[3rem] lg:pl-[1.5rem] lg:ml-[2.5rem] md:pl-[1.5rem] md:ml-[2rem] pl-[1rem] ml-[1.5rem] min-h-[5rem] flex flex-col justify-center">
|
||||
<div class="flex items-center space-x-1">
|
||||
<span class={`w-4 h-4 rounded-full mr-[.6rem] ${styles.bgUtilSuccess}`}></span>
|
||||
<span class={styles.tableText}>
|
||||
{$t('t_0_1746782379424')}: {overviewData.value.workflow.active}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1 mt-3">
|
||||
<span class={`w-4 h-4 rounded-full mr-[.6rem] ${styles.bgUtilError}`}></span>
|
||||
<span class={styles.tableText}>
|
||||
{$t('t_4_1746773348957')}: {overviewData.value.workflow.failure}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class={styles.workflowIcon}>
|
||||
<NIcon size="28">
|
||||
<Flow />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
return () => (
|
||||
<div
|
||||
class="w-full p-[24px] mx-auto max-w-[1600px] "
|
||||
style={cssVars.value}
|
||||
>
|
||||
<NSpin show={loading.value}>
|
||||
<div class="flex flex-col h-full gap-[24px]">
|
||||
{/* 概览模块 */}
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-[24px]">
|
||||
{/* 自动化工作流概览卡片 */}
|
||||
<div
|
||||
onClick={() => pushToWorkflow()}
|
||||
class="cursor-pointer relative"
|
||||
>
|
||||
<div
|
||||
class={`absolute right-0 top-0 w-24 h-24 rounded-full opacity-70 -z-10 ${styles.bgUtilDecorative}`}
|
||||
></div>
|
||||
<NCard
|
||||
class="transition-all duration-300 rounded-[1.2rem]"
|
||||
hoverable={true}
|
||||
bordered={false}
|
||||
content-class="!p-[24px] !pl-[45px]"
|
||||
>
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="flex-1">
|
||||
<div class={styles.tableTitle}>
|
||||
{$t("t_2_1746773350970")}
|
||||
</div>
|
||||
<div class="flex items-center xl:space-x-5 lg:space-x-4 md:space-x-3 space-x-3">
|
||||
<div
|
||||
class={`border-r-2 pr-16 mr-16 ${styles.borderColor}`}
|
||||
>
|
||||
<span
|
||||
class={`xl:text-[3rem] lg:text-[2.2rem] md:text-[2rem] text-[1.8rem] font-bold text-color4 ${styles.gradientNumber}`}
|
||||
>
|
||||
{overviewData.value.workflow.count}
|
||||
</span>
|
||||
<p class={styles.tableSubtitle}>
|
||||
{$t("t_3_1746773348798")}
|
||||
</p>
|
||||
</div>
|
||||
<div class="!ml-0 min-h-[5rem] flex flex-col justify-center">
|
||||
<div class="flex items-center space-x-1">
|
||||
<NIcon size="22" class="mr-4 flex">
|
||||
<span
|
||||
class="inline-flex"
|
||||
innerHTML={statusIcons.value.success}
|
||||
/>
|
||||
</NIcon>
|
||||
<div class={`${styles.tableText} flex`}>
|
||||
<span class="min-w-[70px]">
|
||||
{$t("t_0_1746782379424")}
|
||||
</span>
|
||||
<span class="number ml-4 font-bold">
|
||||
{overviewData.value.workflow.active}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1 mt-4">
|
||||
<NIcon size="22" class="mr-4 flex">
|
||||
<span
|
||||
class="inline-flex"
|
||||
innerHTML={statusIcons.value.error}
|
||||
/>
|
||||
</NIcon>
|
||||
<div class={`${styles.tableText} flex`}>
|
||||
<span class="min-w-[70px]">
|
||||
{$t("t_4_1746773348957")}
|
||||
</span>
|
||||
<span class="number ml-4 font-bold">
|
||||
{overviewData.value.workflow.failure}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
|
||||
{/* 证书管理概览卡片 */}
|
||||
<div onClick={() => pushToCertManage()} class="cursor-pointer relative">
|
||||
<div
|
||||
class={`absolute right-0 top-0 w-24 h-24 rounded-full opacity-70 -z-10 ${styles.bgUtilDecorative}`}
|
||||
></div>
|
||||
<NCard class="transition-all duration-300 rounded-[0.6rem]" hoverable={true} bordered={false}>
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="flex-1">
|
||||
<div class={styles.tableText}>{$t('t_2_1744258111238')}</div>
|
||||
<div class="flex items-center xl:space-x-5 lg:space-x-4 md:space-x-3 space-x-3">
|
||||
<div>
|
||||
<span class="xl:text-[2.4rem] lg:text-[2.2rem] md:text-[2rem] text-[1.8rem] font-bold">
|
||||
{overviewData.value.cert.count}
|
||||
</span>
|
||||
<p class={styles.tableText}>{$t('t_3_1746773348798')}</p>
|
||||
</div>
|
||||
<div class="border-l-2 xl:pl-[2rem] xl:ml-[3rem] lg:pl-[1.5rem] lg:ml-[2.5rem] md:pl-[1.5rem] md:ml-[2rem] pl-[1rem] ml-[1.5rem] min-h-[5rem] flex flex-col justify-center">
|
||||
<div class="flex items-center space-x-1">
|
||||
<span class={`w-4 h-4 rounded-full mr-[.6rem] ${styles.bgUtilWarning}`}></span>
|
||||
<span class={styles.tableText}>
|
||||
{$t('t_5_1746773349141')}: {overviewData.value.cert.will}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1 mt-3">
|
||||
<span class={`w-4 h-4 rounded-full mr-[.6rem] ${styles.bgUtilError}`}></span>
|
||||
<span class={styles.tableText}>
|
||||
{$t('t_0_1746001199409')}: {overviewData.value.cert.end}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class={styles.certIcon}>
|
||||
<NIcon size="28">
|
||||
<Certificate20Regular />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
{/* 证书管理概览卡片 */}
|
||||
<div
|
||||
onClick={() => pushToCertManage()}
|
||||
class="cursor-pointer relative"
|
||||
>
|
||||
<div
|
||||
class={`absolute right-0 top-0 w-24 h-24 rounded-full opacity-70 -z-10 ${styles.bgUtilDecorative}`}
|
||||
></div>
|
||||
<NCard
|
||||
class="transition-all duration-300 rounded-[1.2rem]"
|
||||
hoverable={true}
|
||||
bordered={false}
|
||||
content-class="!p-[24px] !pl-[45px]"
|
||||
>
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="flex-1">
|
||||
<div class={styles.tableTitle}>
|
||||
{$t("t_2_1744258111238")}
|
||||
</div>
|
||||
<div class="flex items-center xl:space-x-5 lg:space-x-4 md:space-x-3 space-x-3">
|
||||
<div
|
||||
class={`border-r-2 pr-16 mr-16 ${styles.borderColor}`}
|
||||
>
|
||||
<span
|
||||
class={`xl:text-[3rem] lg:text-[2.2rem] md:text-[2rem] text-[1.8rem] font-bold text-color4 ${styles.gradientNumber}`}
|
||||
>
|
||||
{overviewData.value.cert.count}
|
||||
</span>
|
||||
<p class={styles.tableSubtitle}>
|
||||
{$t("t_3_1746773348798")}
|
||||
</p>
|
||||
</div>
|
||||
<div class="!ml-0 min-h-[5rem] flex flex-col justify-center">
|
||||
<div class="flex items-center space-x-1">
|
||||
<NIcon size="22" class="mr-4 flex">
|
||||
<span
|
||||
class="inline-flex"
|
||||
innerHTML={statusIcons.value.toExpire}
|
||||
/>
|
||||
</NIcon>
|
||||
<div class={`${styles.tableText} flex`}>
|
||||
<span class="min-w-[70px]">
|
||||
{$t("t_5_1746773349141")}
|
||||
</span>
|
||||
<span class="number ml-4 font-bold">
|
||||
{overviewData.value.cert.will}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1 mt-4">
|
||||
<NIcon size="22" class="mr-4 flex">
|
||||
<span
|
||||
class="inline-flex"
|
||||
innerHTML={statusIcons.value.expired}
|
||||
/>
|
||||
</NIcon>
|
||||
<div class={`${styles.tableText} flex`}>
|
||||
<span class="min-w-[70px]">
|
||||
{$t("t_0_1746001199409")}
|
||||
</span>
|
||||
<span class="number ml-4 font-bold">
|
||||
{overviewData.value.cert.end}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
|
||||
{/* 实时监控概览卡片 */}
|
||||
<div onClick={() => pushToMonitor()} class="cursor-pointer relative">
|
||||
<div
|
||||
class={`absolute right-0 top-0 w-24 h-24 rounded-full opacity-70 -z-10 ${styles.bgUtilDecorative}`}
|
||||
></div>
|
||||
<NCard class="transition-all duration-300 rounded-[0.6rem]" hoverable={true} bordered={false}>
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="flex-1">
|
||||
<div class={styles.tableText}>{$t('t_6_1746773349980')}</div>
|
||||
<div class="flex items-center xl:space-x-5 lg:space-x-4 md:space-x-3 space-x-3">
|
||||
<div>
|
||||
<span class="xl:text-[2.4rem] lg:text-[2.2rem] md:text-[2rem] text-[1.8rem] font-bold">
|
||||
{overviewData.value.monitor.count}
|
||||
</span>
|
||||
<p class={styles.tableText}>{$t('t_3_1746773348798')}</p>
|
||||
</div>
|
||||
<div class="border-l-2 xl:pl-[2rem] xl:ml-[3rem] lg:pl-[1.5rem] lg:ml-[2.5rem] md:pl-[1.5rem] md:ml-[2rem] pl-[1rem] ml-[1.5rem] min-h-[5rem] flex flex-col justify-center">
|
||||
<div class="flex items-center space-x-1">
|
||||
<span class={`w-4 h-4 rounded-full mr-[.6rem] ${styles.bgUtilError}`}></span>
|
||||
<span class={styles.tableText}>
|
||||
{$t('t_7_1746773349302')}: {overviewData.value.monitor.exception}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class={styles.monitorIcon}>
|
||||
<NIcon size="28">
|
||||
<CloudMonitoring />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
</div>
|
||||
{/* 实时监控概览卡片 */}
|
||||
<div
|
||||
onClick={() => pushToMonitor()}
|
||||
class="cursor-pointer relative"
|
||||
>
|
||||
<div
|
||||
class={`absolute right-0 top-0 w-24 h-24 rounded-full opacity-70 -z-10 ${styles.bgUtilDecorative}`}
|
||||
></div>
|
||||
<NCard
|
||||
class="transition-all duration-300 rounded-[1.2rem]"
|
||||
hoverable={true}
|
||||
bordered={false}
|
||||
content-class="!p-[24px] !pl-[45px]"
|
||||
>
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="flex-1">
|
||||
<div class={styles.tableTitle}>
|
||||
{$t("t_6_1746773349980")}
|
||||
</div>
|
||||
<div class="flex items-center xl:space-x-5 lg:space-x-4 md:space-x-3 space-x-3">
|
||||
<div
|
||||
class={`border-r-2 pr-16 mr-16 ${styles.borderColor}`}
|
||||
>
|
||||
<span
|
||||
class={`xl:text-[3rem] lg:text-[2.2rem] md:text-[2rem] text-[1.8rem] font-bold text-color4 ${styles.gradientNumber}`}
|
||||
>
|
||||
{overviewData.value.monitor.count}
|
||||
</span>
|
||||
<p class={styles.tableSubtitle}>
|
||||
{$t("t_3_1746773348798")}
|
||||
</p>
|
||||
</div>
|
||||
<div class="!ml-0 min-h-[5rem] flex flex-col justify-center">
|
||||
<div class="flex items-center space-x-1">
|
||||
<NIcon size="22" class="mr-4 flex">
|
||||
<span
|
||||
class="inline-flex"
|
||||
innerHTML={statusIcons.value.abnormal}
|
||||
/>
|
||||
</NIcon>
|
||||
<div class={`${styles.tableText} flex`}>
|
||||
<span class="min-w-[70px]">
|
||||
{$t("t_7_1746773349302")}
|
||||
</span>
|
||||
<span class="number ml-4 font-bold">
|
||||
{overviewData.value.monitor.exception}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 工作流执行列表 */}
|
||||
<NCard class="rounded-[0.6rem] transition-all duration-300" hoverable={true} bordered={false}>
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<div class={styles.tableText}>{$t('t_8_1746773351524')}</div>
|
||||
<NButton text type="primary" onClick={() => pushToWorkflow()} class={styles.viewAllButton}>
|
||||
{$t('t_9_1746773348221')}
|
||||
<NIcon class="ml-1">
|
||||
<ArrowRight />
|
||||
</NIcon>
|
||||
</NButton>
|
||||
</div>
|
||||
{overviewData.value.workflow_history.length > 0 ? (
|
||||
<NDataTable
|
||||
columns={columns}
|
||||
data={overviewData.value.workflow_history}
|
||||
bordered={false}
|
||||
size="small"
|
||||
singleLine={false}
|
||||
rowClassName={() => 'border-none'}
|
||||
class="border-none"
|
||||
style={{
|
||||
'--n-border-color': 'transparent',
|
||||
'--n-border-radius': '0',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<NEmpty description={$t('t_10_1746773351576')} />
|
||||
)}
|
||||
</NCard>
|
||||
{/* 工作流执行列表 */}
|
||||
<NCard
|
||||
class="rounded-[1rem] transition-all duration-300"
|
||||
hoverable={true}
|
||||
bordered={false}
|
||||
content-class="!p-[24px]"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<div class={styles.tableTitle}>{$t("t_8_1746773351524")}</div>
|
||||
<NButton
|
||||
text
|
||||
type="primary"
|
||||
onClick={() => pushToWorkflow()}
|
||||
class={`font-bold gradient-primary-txt`}
|
||||
>
|
||||
{$t("t_9_1746773348221")}
|
||||
</NButton>
|
||||
</div>
|
||||
{overviewData.value.workflow_history.length > 0 ? (
|
||||
<NDataTable
|
||||
columns={columns}
|
||||
data={overviewData.value.workflow_history}
|
||||
bordered={false}
|
||||
size="medium"
|
||||
singleLine={false}
|
||||
rowClassName={() => "border-none"}
|
||||
class="border-none"
|
||||
style={{
|
||||
"--n-border-color": "transparent",
|
||||
"--n-border-radius": "0",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<NEmpty description={$t("t_10_1746773351576")} />
|
||||
)}
|
||||
</NCard>
|
||||
|
||||
{/* 快捷入口区域 */}
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{/* 工作流构建入口 */}
|
||||
<div onClick={() => pushToWorkflow('create')} class="cursor-pointer">
|
||||
<NCard
|
||||
class={`${styles.quickEntryCard} ${styles.workflow} transition-all duration-300`}
|
||||
hoverable={true}
|
||||
bordered={false}
|
||||
>
|
||||
<div class="flex items-center p-6">
|
||||
<div class={`${styles.iconWrapper} mr-6`}>
|
||||
<NIcon size="32">
|
||||
<Flow />
|
||||
</NIcon>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class={`${styles.title} text-[1.8rem] font-medium mb-3`}>{$t('t_11_1746773349054')}</div>
|
||||
<div class={styles.tableText}>{$t('t_12_1746773355641')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
{/* 快捷入口区域 */}
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-[24px]">
|
||||
{/* 工作流构建入口 */}
|
||||
<div
|
||||
onClick={() => pushToWorkflow("create")}
|
||||
class="cursor-pointer"
|
||||
>
|
||||
<NCard
|
||||
class={`${styles.quickEntryCard} transition-all duration-300`}
|
||||
hoverable={true}
|
||||
bordered={false}
|
||||
content-class="!p-0"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<div class={styles.quickEntryImageWrapper}>
|
||||
<NImage
|
||||
class={styles.quickEntryImage}
|
||||
src={quickEntryImgs.value[0]}
|
||||
/>
|
||||
</div>
|
||||
<div class={styles.quickEntryCardContent}>
|
||||
<div
|
||||
class={`${styles.title} text-[1.8rem] font-medium mb-3`}
|
||||
>
|
||||
{$t("t_11_1746773349054")}
|
||||
</div>
|
||||
<div class={`${styles.tableText} text-color5`}>
|
||||
{$t("t_12_1746773355641")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
|
||||
{/* 申请证书入口 */}
|
||||
<div onClick={() => pushToCert()} class="cursor-pointer">
|
||||
<NCard
|
||||
class={`${styles.quickEntryCard} ${styles.cert} transition-all duration-300 rounded-[0.6rem]`}
|
||||
hoverable={true}
|
||||
bordered={false}
|
||||
>
|
||||
<div class="flex items-center p-6">
|
||||
<div class={`${styles.iconWrapper} mr-6`}>
|
||||
<NIcon size="32">
|
||||
<Certificate20Regular />
|
||||
</NIcon>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class={`${styles.title} text-[1.8rem] font-medium mb-3`}>{$t('t_13_1746773349526')}</div>
|
||||
<div class={styles.tableText}>{$t('t_14_1746773355081')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
{/* 申请证书入口 */}
|
||||
<div onClick={() => pushToCert()} class="cursor-pointer">
|
||||
<NCard
|
||||
class={`${styles.quickEntryCard} transition-all duration-300 rounded-[0.8rem]`}
|
||||
hoverable={true}
|
||||
bordered={false}
|
||||
content-class="!p-0"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<div class={styles.quickEntryImageWrapper}>
|
||||
<NImage
|
||||
class={styles.quickEntryImage}
|
||||
src={quickEntryImgs.value[1]}
|
||||
/>
|
||||
</div>
|
||||
<div class={styles.quickEntryCardContent}>
|
||||
<div
|
||||
class={`${styles.title} text-[1.8rem] font-medium mb-3`}
|
||||
>
|
||||
{$t("t_13_1746773349526")}
|
||||
</div>
|
||||
<div class={`${styles.tableText} text-color5`}>
|
||||
{$t("t_14_1746773355081")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
|
||||
{/* 添加监控入口 */}
|
||||
<div onClick={() => pushToMonitor('create')} class="cursor-pointer">
|
||||
<NCard
|
||||
class={`${styles.quickEntryCard} ${styles.monitor} transition-all duration-300 rounded-[0.6rem]`}
|
||||
hoverable={true}
|
||||
bordered={false}
|
||||
>
|
||||
<div class="flex items-center p-6">
|
||||
<div class={`${styles.iconWrapper} mr-6`}>
|
||||
<NIcon size="32">
|
||||
<CloudMonitoring />
|
||||
</NIcon>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class={`${styles.title} text-[1.8rem] font-medium mb-3`}>{$t('t_11_1745289354516')}</div>
|
||||
<div class={styles.tableText}>{$t('t_1_1747019624067')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NSpin>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
{/* 添加监控入口 */}
|
||||
<div
|
||||
onClick={() => pushToMonitor("create")}
|
||||
class="cursor-pointer"
|
||||
>
|
||||
<NCard
|
||||
class={`${styles.quickEntryCard} transition-all duration-300 rounded-[0.8rem]`}
|
||||
hoverable={true}
|
||||
bordered={false}
|
||||
content-class="!p-0"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<div class={styles.quickEntryImageWrapper}>
|
||||
<NImage
|
||||
class={styles.quickEntryImage}
|
||||
src={quickEntryImgs.value[2]}
|
||||
/>
|
||||
</div>
|
||||
<div class={styles.quickEntryCardContent}>
|
||||
<div
|
||||
class={`${styles.title} text-[1.8rem] font-medium mb-3`}
|
||||
>
|
||||
{$t("t_11_1745289354516")}
|
||||
</div>
|
||||
<div class={`${styles.tableText} text-color5`}>
|
||||
{$t("t_1_1747019624067")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NSpin>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,20 +1,43 @@
|
||||
import { useRouter } from 'vue-router';
|
||||
import { onMounted } from 'vue'; // 新增:导入 onMounted
|
||||
import { NTag } from 'naive-ui';
|
||||
import { useTheme } from "@baota/naive-ui/theme";
|
||||
|
||||
// Type Imports
|
||||
import type { Ref } from 'vue'; // 新增:导入 Ref 类型
|
||||
import type { DataTableColumns } from 'naive-ui';
|
||||
import type { OverviewData, WorkflowHistoryItem } from '@/types/public'; // 新增:导入 OverviewData 类型
|
||||
import type { Ref } from "vue"; // 新增:导入 Ref 类型
|
||||
import type { DataTableColumns } from "naive-ui";
|
||||
import type { OverviewData, WorkflowHistoryItem } from "@/types/public"; // 新增:导入 OverviewData 类型
|
||||
|
||||
// Absolute Internal Imports - Utilities
|
||||
import { $t } from '@locales/index';
|
||||
import { $t } from "@locales/index";
|
||||
|
||||
// Relative Internal Imports
|
||||
import { useStore } from './useStore';
|
||||
import { useStore } from "./useStore";
|
||||
|
||||
// Side-effect Imports
|
||||
import styles from './index.module.css';
|
||||
import styles from "./index.module.css";
|
||||
|
||||
// -------------------- 资源导入(顶层) --------------------
|
||||
// 状态图标(raw 内联,避免重复网络请求)
|
||||
import ExecSuccessIconLight from "@/assets/icons/svg/home/exec-success.svg?raw";
|
||||
import ExecErrorIconLight from "@/assets/icons/svg/home/exec-error.svg?raw";
|
||||
import ToExpireIconLight from "@/assets/icons/svg/home/to-expire.svg?raw";
|
||||
import ExpiredIconLight from "@/assets/icons/svg/home/expired.svg?raw";
|
||||
import AbnormalIconLight from "@/assets/icons/svg/home/abnormal.svg?raw";
|
||||
|
||||
import ExecSuccessIconDark from "@/assets/icons/svg/home/exec-dark-success.svg?raw";
|
||||
import ExecErrorIconDark from "@/assets/icons/svg/home/exec-dark-error.svg?raw";
|
||||
import ToExpireIconDark from "@/assets/icons/svg/home/to-dark-expire.svg?raw";
|
||||
import ExpiredIconDark from "@/assets/icons/svg/home/expired-dark.svg?raw";
|
||||
import AbnormalIconDark from "@/assets/icons/svg/home/abnormal-dark.svg?raw";
|
||||
|
||||
// 快捷入口图片
|
||||
import QuickEntry1Light from "@/assets/images/home-quick-entry1.png";
|
||||
import QuickEntry2Light from "@/assets/images/home-quick-entry2.png";
|
||||
import QuickEntry3Light from "@/assets/images/home-quick-entry3.png";
|
||||
import QuickEntry1Dark from "@/assets/images/home-quick-dark1.png";
|
||||
import QuickEntry2Dark from "@/assets/images/home-quick-dark2.png";
|
||||
import QuickEntry3Dark from "@/assets/images/home-quick-dark3.png";
|
||||
|
||||
/**
|
||||
* @interface HomeControllerExposes
|
||||
@@ -30,15 +53,25 @@ import styles from './index.module.css';
|
||||
* @property {() => DataTableColumns<WorkflowHistoryItem>} createColumns - 创建表格列配置。
|
||||
*/
|
||||
interface HomeControllerExposes {
|
||||
overviewData: Ref<OverviewData>;
|
||||
pushToWorkflow: (type?: string) => void;
|
||||
pushToCert: (type?: string) => void;
|
||||
pushToMonitor: (type?: string) => void;
|
||||
pushToCertManage: () => void;
|
||||
getWorkflowStateType: (state: number) => 'success' | 'error' | 'warning' | 'default';
|
||||
getWorkflowStateText: (state: number) => string;
|
||||
formatExecTime: (time: string) => string;
|
||||
createColumns: () => DataTableColumns<WorkflowHistoryItem>;
|
||||
overviewData: Ref<OverviewData>;
|
||||
pushToWorkflow: (type?: string) => void;
|
||||
pushToCert: (type?: string) => void;
|
||||
pushToMonitor: (type?: string) => void;
|
||||
pushToCertManage: () => void;
|
||||
getWorkflowStateType: (
|
||||
state: number
|
||||
) => "success" | "error" | "warning" | "default";
|
||||
getWorkflowStateText: (state: number) => string;
|
||||
formatExecTime: (time: string) => string;
|
||||
createColumns: () => DataTableColumns<WorkflowHistoryItem>;
|
||||
statusIcons: ComputedRef<{
|
||||
success: string;
|
||||
error: string;
|
||||
toExpire: string;
|
||||
expired: string;
|
||||
abnormal: string;
|
||||
}>;
|
||||
quickEntryImgs: ComputedRef<readonly string[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,146 +81,196 @@ interface HomeControllerExposes {
|
||||
* @returns {HomeControllerExposes} 返回首页视图所需的状态和方法。
|
||||
*/
|
||||
export function useController(): HomeControllerExposes {
|
||||
// 路由实例
|
||||
const router = useRouter();
|
||||
// 从 Store 中获取状态和方法
|
||||
const { overviewData, fetchOverviewData } = useStore();
|
||||
// 路由实例
|
||||
const router = useRouter();
|
||||
// 从 Store 中获取状态和方法
|
||||
const { overviewData, fetchOverviewData } = useStore();
|
||||
const { isDark } = useTheme();
|
||||
|
||||
// -------------------- 业务逻辑处理 --------------------
|
||||
/**
|
||||
* 获取工作流状态对应的标签类型。
|
||||
* @function getWorkflowStateType
|
||||
* @param {number} state - 工作流状态值。
|
||||
* @returns {'success' | 'error' | 'warning' | 'default'} NTag 组件的 type 属性值。
|
||||
*/
|
||||
function getWorkflowStateType(state: number): 'success' | 'error' | 'warning' | 'default' {
|
||||
switch (state) {
|
||||
case 1:
|
||||
return 'success'; // 成功状态
|
||||
case 0:
|
||||
return 'warning'; // 正在运行状态 (根据原代码逻辑,0是warning,-1是error)
|
||||
case -1:
|
||||
return 'error'; // 失败状态
|
||||
default:
|
||||
return 'default'; // 未知状态
|
||||
}
|
||||
}
|
||||
// -------------------- 图标与图片资源(集中管理) --------------------
|
||||
const LIGHT_STATUS_ICONS = {
|
||||
success: ExecSuccessIconLight,
|
||||
error: ExecErrorIconLight,
|
||||
toExpire: ToExpireIconLight,
|
||||
expired: ExpiredIconLight,
|
||||
abnormal: AbnormalIconLight,
|
||||
} as const;
|
||||
const DARK_STATUS_ICONS: typeof LIGHT_STATUS_ICONS = {
|
||||
success: ExecSuccessIconDark,
|
||||
error: ExecErrorIconDark,
|
||||
toExpire: ToExpireIconDark,
|
||||
expired: ExpiredIconDark,
|
||||
abnormal: AbnormalIconDark,
|
||||
};
|
||||
const statusIcons = computed(() =>
|
||||
isDark.value ? DARK_STATUS_ICONS : LIGHT_STATUS_ICONS
|
||||
);
|
||||
|
||||
/**
|
||||
* 获取工作流状态对应的文本说明。
|
||||
* @function getWorkflowStateText
|
||||
* @param {number} state - 工作流状态值。
|
||||
* @returns {string} 状态的中文描述。
|
||||
*/
|
||||
function getWorkflowStateText(state: number): string {
|
||||
switch (state) {
|
||||
case 1:
|
||||
return $t('t_8_1745227838023');
|
||||
case 0:
|
||||
return $t('t_0_1747795605426');
|
||||
case -1:
|
||||
return $t('t_9_1745227838305');
|
||||
default:
|
||||
return $t('t_11_1745227838422');
|
||||
}
|
||||
}
|
||||
// 快捷入口图片(静态导入 + 浏览器缓存)
|
||||
const LIGHT_QUICK_ENTRY_IMAGES = [
|
||||
QuickEntry1Light,
|
||||
QuickEntry2Light,
|
||||
QuickEntry3Light,
|
||||
] as const;
|
||||
const DARK_QUICK_ENTRY_IMAGES: typeof LIGHT_QUICK_ENTRY_IMAGES = [
|
||||
QuickEntry1Dark,
|
||||
QuickEntry2Dark,
|
||||
QuickEntry3Dark,
|
||||
];
|
||||
const quickEntryImgs = computed<readonly string[]>(() =>
|
||||
isDark.value ? DARK_QUICK_ENTRY_IMAGES : LIGHT_QUICK_ENTRY_IMAGES
|
||||
);
|
||||
|
||||
/**
|
||||
* 格式化执行时间为本地化的日期时间字符串。
|
||||
* @function formatExecTime
|
||||
* @param {string} time - ISO 格式的时间字符串。
|
||||
* @returns {string} 格式化后的本地时间字符串。
|
||||
*/
|
||||
function formatExecTime(time: string): string {
|
||||
return new Date(time).toLocaleString();
|
||||
}
|
||||
// -------------------- 业务逻辑处理 --------------------
|
||||
/**
|
||||
* 获取工作流状态对应的标签类型。
|
||||
* @function getWorkflowStateType
|
||||
* @param {number} state - 工作流状态值。
|
||||
* @returns {'success' | 'error' | 'warning' | 'default'} NTag 组件的 type 属性值。
|
||||
*/
|
||||
function getWorkflowStateType(
|
||||
state: number
|
||||
): "success" | "error" | "warning" | "default" {
|
||||
switch (state) {
|
||||
case 1:
|
||||
return "success"; // 成功状态
|
||||
case 0:
|
||||
return "warning"; // 正在运行状态 (根据原代码逻辑,0是warning,-1是error)
|
||||
case -1:
|
||||
return "error"; // 失败状态
|
||||
default:
|
||||
return "default"; // 未知状态
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建工作流历史表格列配置。
|
||||
* @function createColumns
|
||||
* @returns {DataTableColumns<WorkflowHistoryItem>} 工作流历史表格列配置。
|
||||
*/
|
||||
function createColumns(): DataTableColumns<WorkflowHistoryItem> {
|
||||
return [
|
||||
{
|
||||
title: $t('t_2_1745289353944'), // 名称
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: $t('t_0_1746590054456'), // 状态
|
||||
key: 'state',
|
||||
render: (row: WorkflowHistoryItem) => {
|
||||
const stateType = getWorkflowStateType(row.state);
|
||||
const stateText = getWorkflowStateText(row.state);
|
||||
return (
|
||||
<NTag type={stateType} size="small" class={`${styles.stateText} ${styles[stateType]}`}>
|
||||
{stateText}
|
||||
</NTag>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: $t('t_1_1746590060448'), // 模式
|
||||
key: 'mode',
|
||||
render: (row: WorkflowHistoryItem) => {
|
||||
return <span class={styles.tableText}>{row.mode || $t('t_11_1745227838422')}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: $t('t_4_1745227838558'), // 执行时间
|
||||
key: 'exec_time',
|
||||
render: (row: WorkflowHistoryItem) => <span class={styles.tableText}>{formatExecTime(row.exec_time)}</span>,
|
||||
},
|
||||
];
|
||||
}
|
||||
/**
|
||||
* 获取工作流状态对应的文本说明。
|
||||
* @function getWorkflowStateText
|
||||
* @param {number} state - 工作流状态值。
|
||||
* @returns {string} 状态的中文描述。
|
||||
*/
|
||||
function getWorkflowStateText(state: number): string {
|
||||
switch (state) {
|
||||
case 1:
|
||||
return $t("t_8_1745227838023");
|
||||
case 0:
|
||||
return $t("t_0_1747795605426");
|
||||
case -1:
|
||||
return $t("t_9_1745227838305");
|
||||
default:
|
||||
return $t("t_11_1745227838422");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至工作流构建页面。
|
||||
* @function pushToWorkflow
|
||||
* @param {string} [type=''] - 类型参数,可选。
|
||||
*/
|
||||
function pushToWorkflow(type: string = ''): void {
|
||||
router.push(`/auto-deploy${type ? `?type=${type}` : ''}`);
|
||||
}
|
||||
/**
|
||||
* 格式化执行时间为本地化的日期时间字符串。
|
||||
* @function formatExecTime
|
||||
* @param {string} time - ISO 格式的时间字符串。
|
||||
* @returns {string} 格式化后的本地时间字符串。
|
||||
*/
|
||||
function formatExecTime(time: string): string {
|
||||
return new Date(time).toLocaleString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至申请证书页面。
|
||||
* @function pushToCert
|
||||
* @param {string} [type=''] - 类型参数,可选。
|
||||
*/
|
||||
function pushToCert(type: string = ''): void {
|
||||
router.push(`/cert-apply${type ? `?type=${type}` : ''}`);
|
||||
}
|
||||
/**
|
||||
* 创建工作流历史表格列配置。
|
||||
* @function createColumns
|
||||
* @returns {DataTableColumns<WorkflowHistoryItem>} 工作流历史表格列配置。
|
||||
*/
|
||||
function createColumns(): DataTableColumns<WorkflowHistoryItem> {
|
||||
return [
|
||||
{
|
||||
title: () => <span class="font-[600]">{$t("t_2_1745289353944")}</span>, // 名称
|
||||
key: "name",
|
||||
},
|
||||
{
|
||||
title: () => <span class="font-[600]">{$t("t_0_1746590054456")}</span>, // 状态
|
||||
key: "state",
|
||||
render: (row: WorkflowHistoryItem) => {
|
||||
const stateType = getWorkflowStateType(row.state);
|
||||
const stateText = getWorkflowStateText(row.state);
|
||||
return (
|
||||
<NTag
|
||||
type={stateType}
|
||||
size="small"
|
||||
round
|
||||
class={`${styles.stateText} ${styles[stateType]}`}
|
||||
>
|
||||
{stateText}
|
||||
</NTag>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: () => <span class="font-[600]">{$t("t_1_1746590060448")}</span>, // 模式
|
||||
key: "mode",
|
||||
render: (row: WorkflowHistoryItem) => {
|
||||
return (
|
||||
<span class={styles.tableText}>
|
||||
{row.mode || $t("t_11_1745227838422")}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: () => <span class="font-[600]">{$t("t_4_1745227838558")}</span>, // 执行时间
|
||||
key: "exec_time",
|
||||
render: (row: WorkflowHistoryItem) => (
|
||||
<span class={styles.tableText}>{formatExecTime(row.exec_time)}</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至证书管理页面。
|
||||
* @function pushToCertManage
|
||||
*/
|
||||
function pushToCertManage(): void {
|
||||
router.push(`/cert-manage`);
|
||||
}
|
||||
/**
|
||||
* 跳转至工作流构建页面。
|
||||
* @function pushToWorkflow
|
||||
* @param {string} [type=''] - 类型参数,可选。
|
||||
*/
|
||||
function pushToWorkflow(type: string = ""): void {
|
||||
router.push(`/auto-deploy${type ? `?type=${type}` : ""}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至添加监控页面。
|
||||
* @function pushToMonitor
|
||||
* @param {string} [type=''] - 类型参数,可选。
|
||||
*/
|
||||
function pushToMonitor(type: string = ''): void {
|
||||
router.push(`/monitor${type ? `?type=${type}` : ''}`);
|
||||
}
|
||||
/**
|
||||
* 跳转至申请证书页面。
|
||||
* @function pushToCert
|
||||
* @param {string} [type=''] - 类型参数,可选。
|
||||
*/
|
||||
function pushToCert(type: string = ""): void {
|
||||
router.push(`/cert-apply${type ? `?type=${type}` : ""}`);
|
||||
}
|
||||
|
||||
onMounted(fetchOverviewData);
|
||||
/**
|
||||
* 跳转至证书管理页面。
|
||||
* @function pushToCertManage
|
||||
*/
|
||||
function pushToCertManage(): void {
|
||||
router.push(`/cert-manage`);
|
||||
}
|
||||
|
||||
// 暴露状态和方法给视图使用
|
||||
return {
|
||||
overviewData,
|
||||
pushToWorkflow,
|
||||
pushToCert,
|
||||
pushToMonitor,
|
||||
pushToCertManage,
|
||||
getWorkflowStateType,
|
||||
getWorkflowStateText,
|
||||
formatExecTime,
|
||||
createColumns,
|
||||
};
|
||||
/**
|
||||
* 跳转至添加监控页面。
|
||||
* @function pushToMonitor
|
||||
* @param {string} [type=''] - 类型参数,可选。
|
||||
*/
|
||||
function pushToMonitor(type: string = ""): void {
|
||||
router.push(`/monitor${type ? `?type=${type}` : ""}`);
|
||||
}
|
||||
|
||||
onMounted(fetchOverviewData);
|
||||
|
||||
// 暴露状态和方法给视图使用
|
||||
return {
|
||||
overviewData,
|
||||
pushToWorkflow,
|
||||
pushToCert,
|
||||
pushToMonitor,
|
||||
pushToCertManage,
|
||||
getWorkflowStateType,
|
||||
getWorkflowStateText,
|
||||
formatExecTime,
|
||||
createColumns,
|
||||
statusIcons,
|
||||
quickEntryImgs,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
.sider {
|
||||
@apply h-screen shadow-lg z-10 transition-all duration-300 ease-in-out;
|
||||
/* 移动端默认行为通过 NLayoutSider 的 collapsed 属性和 TSX 逻辑控制 */
|
||||
background-color: var(--n-sider-color, var(--n-body-color));
|
||||
}
|
||||
|
||||
/* Logo容器样式 */
|
||||
@@ -77,6 +78,46 @@
|
||||
color: var(--n-text-color-secondary); /* Naive UI 次级文字颜色 */
|
||||
}
|
||||
|
||||
/* 主题切换按钮容器 */
|
||||
.systemInfo > div {
|
||||
@apply mr-2 sm:mr-3;
|
||||
}
|
||||
|
||||
.themeSelector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.themeSelectorTrigger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
padding: 0.8rem 1.6rem;
|
||||
border-radius: 999px;
|
||||
border: none;
|
||||
/* background-color: var(--theme-select-bg); */
|
||||
color: var(--n-text-color-2);
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.themeSelectorTrigger:hover {
|
||||
color: var(--n-text-color-1);
|
||||
background-color: var(--theme-select-bg);
|
||||
}
|
||||
|
||||
.themeSelectorLabel {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.themeSelectorArrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
||||
/* 内容区域样式 */
|
||||
.content {
|
||||
|
||||
@@ -10,14 +10,26 @@ import {
|
||||
watch,
|
||||
onUnmounted,
|
||||
} from 'vue' // 添加 watch, onUnmounted
|
||||
import { NBadge, NIcon, NLayout, NLayoutContent, NLayoutHeader, NLayoutSider, NMenu, NTooltip } from 'naive-ui'
|
||||
import {
|
||||
NBadge,
|
||||
NDropdown,
|
||||
NIcon,
|
||||
NLayout,
|
||||
NLayoutContent,
|
||||
NLayoutHeader,
|
||||
NLayoutSider,
|
||||
NMenu,
|
||||
NTooltip,
|
||||
type DropdownOption,
|
||||
} from "naive-ui";
|
||||
import { RouterView } from 'vue-router'
|
||||
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@vicons/antd'
|
||||
import { ChevronDown, MoonOutline, SunnyOutline } from "@vicons/ionicons5";
|
||||
import { useMediaQuery } from '@vueuse/core' // 引入 useMediaQuery
|
||||
|
||||
// 内部模块导入 - Hooks/Composables
|
||||
import { useThemeCssVar } from '@baota/naive-ui/theme'
|
||||
import { useController } from './useController'
|
||||
import { useThemeCssVar, useTheme } from "@baota/naive-ui/theme";
|
||||
import { useController } from "./useController";
|
||||
// 内部模块导入 - 工具函数
|
||||
import { $t } from '@locales/index'
|
||||
// 内部模块导入 - 样式
|
||||
@@ -38,6 +50,7 @@ export default defineComponent({
|
||||
setup() {
|
||||
const { menuItems, menuActive, isCollapsed, toggleCollapse, handleExpand, handleCollapse, updateMenuActive } =
|
||||
useController()
|
||||
const { themeActive } = useTheme();
|
||||
// 确保所有需要的颜色变量都已在 useThemeCssVar 中声明,或直接在 CSS Module 中使用 var(--n-...)
|
||||
const cssVars = useThemeCssVar([
|
||||
'bodyColor', // --n-color 通常是 bodyColor
|
||||
@@ -46,6 +59,7 @@ export default defineComponent({
|
||||
'textColorBase',
|
||||
'textColor1',
|
||||
'textColor2',
|
||||
'textColor3',
|
||||
'textColorSecondary',
|
||||
'actionColor',
|
||||
'layoutContentBackgroundColor',
|
||||
@@ -57,7 +71,7 @@ export default defineComponent({
|
||||
const hasUpdate = ref(false)
|
||||
const versionData = ref<VersionData | null>(null)
|
||||
const showUpdateModal = ref(false)
|
||||
const checkTimer = ref<NodeJS.Timeout | null>(null)
|
||||
const checkTimer = ref<number | null>(null)
|
||||
|
||||
// 版本检查API
|
||||
const versionApi = getVersion()
|
||||
@@ -156,117 +170,220 @@ export default defineComponent({
|
||||
// NMenu 的折叠状态 (此处的 menuCollapsedState 变量名可以替换为 nMenuCollapsedProp)
|
||||
// const menuCollapsedState = computed(() => { ... }) // 旧的,将被 nMenuCollapsedProp 替代
|
||||
|
||||
return () => (
|
||||
<NLayout class={styles.layoutContainer} hasSider style={cssVars.value}>
|
||||
<NLayoutSider
|
||||
width={siderWidth.value} // 在移动端,宽度始终是展开时的宽度
|
||||
collapsed={nLayoutSiderCollapsedProp.value} // 使用新的计算属性
|
||||
showTrigger={false}
|
||||
collapseMode="width"
|
||||
collapsedWidth={siderCollapsedWidth.value} // 桌面端折叠宽度及 NMenu 折叠宽度参考
|
||||
onCollapse={handleCollapse}
|
||||
onExpand={handleExpand}
|
||||
class={[styles.sider, siderDynamicClass.value].join(' ')}
|
||||
bordered
|
||||
>
|
||||
<div
|
||||
class={`${styles.logoContainer} ${
|
||||
// Logo 容器的 'active' 状态 (仅在桌面端且折叠时应用)
|
||||
// 在移动端,由于 NLayoutSider 自身宽度不变,不应用 active 样式来改变 Logo 区域布局
|
||||
(isMobile.value ? false : isCollapsed.value) ? styles.logoContainerActive : ''
|
||||
}`}
|
||||
>
|
||||
{/* Logo 显示逻辑 */}
|
||||
{(isMobile.value ? false : isCollapsed.value) ? (
|
||||
// 折叠时的 Logo (仅桌面端)
|
||||
<div class="flex items-center justify-center w-full h-full">
|
||||
<img src="/static/images/logo.png" alt="logo" class="h-8 w-8" />
|
||||
</div>
|
||||
) : (
|
||||
// 展开时的 Logo (桌面端展开时,或移动端侧边栏可见时)
|
||||
<div class={styles.logoContainerText}>
|
||||
<img src="/static/images/logo.png" alt="logo" class="h-8 w-8 mr-2 sm:mr-3" />
|
||||
<span class={`${styles.logoText} ml-0 font-bold`}>{$t('t_1_1744164835667')}</span>
|
||||
</div>
|
||||
)}
|
||||
{/* 桌面端展开状态下的内部折叠按钮 */}
|
||||
{!isCollapsed.value && !isMobile.value && (
|
||||
<NTooltip placement="right" trigger="hover">
|
||||
{{
|
||||
trigger: () => (
|
||||
<div class={styles.menuToggleButton} onClick={() => toggleCollapse()}>
|
||||
<NIcon size={20}>
|
||||
<MenuFoldOutlined />
|
||||
</NIcon>{' '}
|
||||
{/* 图标大小调整为 20 */}
|
||||
</div>
|
||||
),
|
||||
default: () => <span>{$t('t_4_1744098802046')}</span>,
|
||||
}}
|
||||
</NTooltip>
|
||||
)}
|
||||
</div>
|
||||
<NMenu
|
||||
value={menuActive.value}
|
||||
onUpdateValue={(key: string, item: any) => {
|
||||
updateMenuActive(key as any) // 保留原有的菜单激活逻辑
|
||||
// 如果是移动端并且菜单当前是展开状态,则关闭菜单
|
||||
if (isMobile.value && !isCollapsed.value) {
|
||||
isCollapsed.value = true // 直接设置 isCollapsed 为 true 来关闭菜单
|
||||
}
|
||||
}}
|
||||
options={menuItems.value}
|
||||
class="border-none"
|
||||
collapsed={nMenuCollapsedProp.value} // NMenu 的折叠状态
|
||||
collapsedWidth={siderCollapsedWidth.value}
|
||||
collapsedIconSize={22}
|
||||
/>
|
||||
</NLayoutSider>
|
||||
const themeLabelMap: Record<string, string> = {
|
||||
defaultLight: "Default",
|
||||
defaultDark: "Gold",
|
||||
};
|
||||
|
||||
<NLayout>
|
||||
<NLayoutHeader class={styles.header}>
|
||||
{/* 移动端或桌面端侧边栏折叠时,在头部左侧显示展开/收起按钮 */}
|
||||
{(isMobile.value || (!isMobile.value && isCollapsed.value)) && (
|
||||
<div class="mr-auto">
|
||||
<NTooltip placement="right" trigger="hover">
|
||||
{{
|
||||
trigger: () => (
|
||||
<div class={styles.headerMenuToggleButton} onClick={() => toggleCollapse()}>
|
||||
<NIcon size={20}>{isCollapsed.value ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}</NIcon>
|
||||
</div>
|
||||
),
|
||||
default: () => <span>展开主菜单</span>,
|
||||
}}
|
||||
</NTooltip>
|
||||
</div>
|
||||
)}
|
||||
<div class={styles.systemInfo}>
|
||||
<NBadge value={1} show={hasUpdate.value} dot>
|
||||
<span
|
||||
class="px-[.8rem] sm:px-[.5rem] py-[.4rem] cursor-pointer hover:text-primary transition-colors text-[1.4rem] font-medium"
|
||||
onClick={handleVersionClick}
|
||||
>
|
||||
{versionData.value && versionData.value.version}
|
||||
</span>
|
||||
</NBadge>
|
||||
</div>
|
||||
</NLayoutHeader>
|
||||
<NLayoutContent class={styles.content}>
|
||||
<RouterView>
|
||||
{({ Component }: { Component: ComponentType }) => (
|
||||
<Transition name="fade" mode="out-in">
|
||||
{Component && h(Component)}
|
||||
</Transition>
|
||||
)}
|
||||
</RouterView>
|
||||
</NLayoutContent>
|
||||
</NLayout>
|
||||
{/* 移动端菜单展开时的背景遮罩 */}
|
||||
{showBackdrop.value && <div class={styles.mobileMenuBackdrop} onClick={() => toggleCollapse()}></div>}
|
||||
const themeDropdownOptions: DropdownOption[] = [
|
||||
{
|
||||
label: "Default",
|
||||
key: "defaultLight",
|
||||
icon: () => (
|
||||
<NIcon size={16}>
|
||||
<SunnyOutline />
|
||||
</NIcon>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Gold",
|
||||
key: "defaultDark",
|
||||
icon: () => (
|
||||
<NIcon size={16}>
|
||||
<MoonOutline />
|
||||
</NIcon>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
{/* 更新日志弹窗 */}
|
||||
<UpdateLogModal v-model:show={showUpdateModal.value} versionData={versionData.value} />
|
||||
</NLayout>
|
||||
)
|
||||
const handleThemeSelect = (key: string | number) => {
|
||||
if (typeof key !== "string") return;
|
||||
if (themeActive.value === key) return;
|
||||
themeActive.value = key;
|
||||
};
|
||||
|
||||
return () => {
|
||||
const isGoldTheme = themeActive.value === "defaultDark";
|
||||
const ThemeIcon = isGoldTheme ? MoonOutline : SunnyOutline;
|
||||
const currentLabel = themeLabelMap[themeActive.value] || "Default";
|
||||
|
||||
return (
|
||||
<NLayout class={styles.layoutContainer} hasSider style={cssVars.value}>
|
||||
<svg width="0" height="0" style="position: absolute">
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="menu-active-icon-gradient"
|
||||
x1="0%"
|
||||
y1="0%"
|
||||
x2="100%"
|
||||
y2="100%"
|
||||
>
|
||||
<stop offset="0%" stop-color="#9C6240" />
|
||||
<stop offset="100%" stop-color="#FFCF76" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<NLayoutSider
|
||||
width={siderWidth.value} // 在移动端,宽度始终是展开时的宽度
|
||||
collapsed={nLayoutSiderCollapsedProp.value} // 使用新的计算属性
|
||||
showTrigger={false}
|
||||
collapseMode="width"
|
||||
collapsedWidth={siderCollapsedWidth.value} // 桌面端折叠宽度及 NMenu 折叠宽度参考
|
||||
onCollapse={handleCollapse}
|
||||
onExpand={handleExpand}
|
||||
class={[styles.sider, siderDynamicClass.value].join(" ")}
|
||||
bordered
|
||||
>
|
||||
<div
|
||||
class={`${styles.logoContainer} ${
|
||||
// Logo 容器的 'active' 状态 (仅在桌面端且折叠时应用)
|
||||
// 在移动端,由于 NLayoutSider 自身宽度不变,不应用 active 样式来改变 Logo 区域布局
|
||||
(isMobile.value ? false : isCollapsed.value)
|
||||
? styles.logoContainerActive
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
{/* Logo 显示逻辑 */}
|
||||
{(isMobile.value ? false : isCollapsed.value) ? (
|
||||
// 折叠时的 Logo (仅桌面端)
|
||||
<div class="flex items-center justify-center w-full h-full">
|
||||
<img
|
||||
src={`/static/images/logo${isGoldTheme ? '-dark' : ''}.png`}
|
||||
alt="logo"
|
||||
class="h-8 w-8"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
// 展开时的 Logo (桌面端展开时,或移动端侧边栏可见时)
|
||||
<div class={styles.logoContainerText}>
|
||||
<img
|
||||
src={`/static/images/logo${isGoldTheme ? '-dark' : ''}.png`}
|
||||
alt="logo"
|
||||
class="h-8 w-8 mr-2 sm:mr-3"
|
||||
/>
|
||||
<span class={`${styles.logoText} ml-0 font-bold`}>
|
||||
{$t("t_1_1744164835667")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{/* 桌面端展开状态下的内部折叠按钮 */}
|
||||
{!isCollapsed.value && !isMobile.value && (
|
||||
<NTooltip placement="right" trigger="hover">
|
||||
{{
|
||||
trigger: () => (
|
||||
<div
|
||||
class={styles.menuToggleButton}
|
||||
onClick={() => toggleCollapse()}
|
||||
>
|
||||
<NIcon size={20}>
|
||||
<MenuFoldOutlined />
|
||||
</NIcon>{" "}
|
||||
{/* 图标大小调整为 20 */}
|
||||
</div>
|
||||
),
|
||||
default: () => <span>{$t("t_4_1744098802046")}</span>,
|
||||
}}
|
||||
</NTooltip>
|
||||
)}
|
||||
</div>
|
||||
<NMenu
|
||||
value={menuActive.value}
|
||||
onUpdateValue={(key: string, item: any) => {
|
||||
updateMenuActive(key as any); // 保留原有的菜单激活逻辑
|
||||
// 如果是移动端并且菜单当前是展开状态,则关闭菜单
|
||||
if (isMobile.value && !isCollapsed.value) {
|
||||
isCollapsed.value = true; // 直接设置 isCollapsed 为 true 来关闭菜单
|
||||
}
|
||||
}}
|
||||
options={menuItems.value}
|
||||
class="border-none"
|
||||
collapsed={nMenuCollapsedProp.value} // NMenu 的折叠状态
|
||||
collapsedWidth={siderCollapsedWidth.value}
|
||||
collapsedIconSize={22}
|
||||
/>
|
||||
</NLayoutSider>
|
||||
|
||||
<NLayout>
|
||||
<NLayoutHeader class={styles.header}>
|
||||
{/* 移动端或桌面端侧边栏折叠时,在头部左侧显示展开/收起按钮 */}
|
||||
{(isMobile.value || (!isMobile.value && isCollapsed.value)) && (
|
||||
<div class="mr-auto">
|
||||
<NTooltip placement="right" trigger="hover">
|
||||
{{
|
||||
trigger: () => (
|
||||
<div
|
||||
class={styles.headerMenuToggleButton}
|
||||
onClick={() => toggleCollapse()}
|
||||
>
|
||||
<NIcon size={20}>
|
||||
{isCollapsed.value ? (
|
||||
<MenuUnfoldOutlined />
|
||||
) : (
|
||||
<MenuFoldOutlined />
|
||||
)}
|
||||
</NIcon>
|
||||
</div>
|
||||
),
|
||||
default: () => <span>展开主菜单</span>,
|
||||
}}
|
||||
</NTooltip>
|
||||
</div>
|
||||
)}
|
||||
<div class={styles.systemInfo}>
|
||||
<div class={styles.themeSelector}>
|
||||
<NDropdown
|
||||
trigger="click"
|
||||
options={themeDropdownOptions}
|
||||
onSelect={handleThemeSelect}
|
||||
>
|
||||
<div class={styles.themeSelectorTrigger}>
|
||||
<NIcon size={isGoldTheme ? 16 : 18}>
|
||||
<ThemeIcon />
|
||||
</NIcon>
|
||||
<span class={styles.themeSelectorLabel}>
|
||||
{currentLabel}
|
||||
</span>
|
||||
<NIcon size={18} class={styles.themeSelectorArrow}>
|
||||
<ChevronDown />
|
||||
</NIcon>
|
||||
</div>
|
||||
</NDropdown>
|
||||
</div>
|
||||
<NBadge value={1} show={hasUpdate.value} dot>
|
||||
<span
|
||||
class="px-[.8rem] sm:px-[.5rem] py-[.4rem] cursor-pointer hover:text-primary transition-colors text-[1.4rem] font-medium"
|
||||
onClick={handleVersionClick}
|
||||
>
|
||||
{versionData.value && versionData.value.version}
|
||||
</span>
|
||||
</NBadge>
|
||||
</div>
|
||||
</NLayoutHeader>
|
||||
<NLayoutContent class={styles.content}>
|
||||
<RouterView>
|
||||
{({ Component }: { Component: ComponentType }) => (
|
||||
<Transition name="fade" mode="out-in">
|
||||
{Component && h(Component)}
|
||||
</Transition>
|
||||
)}
|
||||
</RouterView>
|
||||
</NLayoutContent>
|
||||
</NLayout>
|
||||
{/* 移动端菜单展开时的背景遮罩 */}
|
||||
{showBackdrop.value && (
|
||||
<div
|
||||
class={styles.mobileMenuBackdrop}
|
||||
onClick={() => toggleCollapse()}
|
||||
></div>
|
||||
)}
|
||||
|
||||
{/* 更新日志弹窗 */}
|
||||
<UpdateLogModal
|
||||
v-model:show={showUpdateModal.value}
|
||||
versionData={versionData.value}
|
||||
/>
|
||||
</NLayout>
|
||||
);
|
||||
};
|
||||
},
|
||||
})
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
|
||||
/* 暗色模式样式 */
|
||||
:global(.defaultDark) {
|
||||
.coreStatusIcon {
|
||||
background-color: transparent!important;
|
||||
}
|
||||
.coreStatusCard {
|
||||
background-color: #171717;
|
||||
}
|
||||
|
||||
.coreStatusCardSuccess {
|
||||
position: relative;
|
||||
border: none !important;
|
||||
background-color: transparent;
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 0.75rem;
|
||||
padding: 1px;
|
||||
background: var(--menu-active-gradient);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
.coreStatusIconSuccess {
|
||||
position: relative;
|
||||
background-color: transparent !important;
|
||||
:global(.n-icon) {
|
||||
svg,
|
||||
svg path {
|
||||
fill: url(#menu-active-icon-gradient) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.coreStatusLabelSuccess {
|
||||
background: var(--menu-active-gradient) !important;
|
||||
-webkit-background-clip: text !important;
|
||||
background-clip: text !important;
|
||||
-webkit-text-fill-color: transparent !important;
|
||||
color: transparent !important;
|
||||
}
|
||||
|
||||
.coreStatusCardError {
|
||||
position: relative;
|
||||
border: none !important;
|
||||
background-color: transparent !important;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 0.75rem;
|
||||
padding: 1px;
|
||||
background: var(--n-error-primary-color);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
.coreStatusIconError {
|
||||
background-color: transparent !important;
|
||||
:global(.n-icon) {
|
||||
svg,
|
||||
svg path {
|
||||
fill: var(--n-error-primary-color) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.coreStatusTextError {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.coreStatusLabelError {
|
||||
color: var(--n-error-primary-color) !important;
|
||||
}
|
||||
|
||||
.headerIcon {
|
||||
svg,
|
||||
svg path {
|
||||
fill: url(#menu-active-icon-gradient) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ import { defineComponent, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { NButton, NCard, NSpin, NIcon, NSpace, NText, NEmpty, NDataTable, NPagination } from 'naive-ui'
|
||||
import { ArrowLeft, Information, ErrorOutline } from '@vicons/carbon'
|
||||
import { useThemeCssVar } from '@baota/naive-ui/theme'
|
||||
import { CloseCircleOutlined, ClockCircleOutlined, CheckCircleOutlined, GlobalOutlined } from '@vicons/antd'
|
||||
import { useThemeCssVar, useTheme } from '@baota/naive-ui/theme'
|
||||
import { useTable } from '@baota/naive-ui/hooks'
|
||||
|
||||
// 工具和钩子
|
||||
@@ -12,6 +13,9 @@ import { useError } from '@baota/hooks/error'
|
||||
import { getMonitorDetail, getMonitorErrorRecord } from '@/api/monitor'
|
||||
import type { MonitorDetailInfo, ErrorRecord, GetErrorRecordParams, CertChainNode } from '@/types/monitor'
|
||||
|
||||
// 样式
|
||||
import styles from './index.module.css'
|
||||
|
||||
/**
|
||||
* 错误列表卡片组件
|
||||
*/
|
||||
@@ -25,7 +29,6 @@ const ErrorListCard = defineComponent({
|
||||
},
|
||||
setup(props) {
|
||||
const { handleError } = useError()
|
||||
|
||||
/**
|
||||
* 格式化日期时间
|
||||
*/
|
||||
@@ -37,7 +40,7 @@ const ErrorListCard = defineComponent({
|
||||
// 错误记录表格配置
|
||||
const errorColumns = [
|
||||
{
|
||||
title: '错误时间',
|
||||
title: () => <span class="font-semibold">错误时间</span>,
|
||||
key: 'create_time',
|
||||
width: 200,
|
||||
render: (row: ErrorRecord) => (
|
||||
@@ -45,7 +48,7 @@ const ErrorListCard = defineComponent({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '错误消息',
|
||||
title: () => <span class="font-semibold">错误消息</span>,
|
||||
key: 'msg',
|
||||
render: (row: ErrorRecord) => (
|
||||
<div class="text-[1.3rem] sm:text-[1.4rem] text-red-600 dark:text-red-400 break-words leading-relaxed">
|
||||
@@ -105,8 +108,8 @@ const ErrorListCard = defineComponent({
|
||||
|
||||
return () => (
|
||||
<NCard
|
||||
title="错误列表"
|
||||
class="h-fit [&_.n-card-header_.n-card-header__main]:text-[1.5rem] [&_.n-card-header_.n-card-header__main]:font-medium"
|
||||
title={ () => <h1 class="text-[1.8rem] font-semibold">错误列表</h1> }
|
||||
class="h-fit [&_.n-card-header_.n-card-header__main]:text-[1.5rem] [&_.n-card-header_.n-card-header__main]:font-medium !bg-[var(--content-bg-base)]"
|
||||
bordered
|
||||
>
|
||||
{{
|
||||
@@ -160,6 +163,7 @@ export default defineComponent({
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { handleError } = useError()
|
||||
const { isDark } = useTheme();
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
@@ -176,6 +180,7 @@ export default defineComponent({
|
||||
'errorColor',
|
||||
'warningColor',
|
||||
'primaryColor',
|
||||
'textColorSecondary',
|
||||
])
|
||||
|
||||
/**
|
||||
@@ -241,16 +246,130 @@ export default defineComponent({
|
||||
* 获取剩余天数的颜色
|
||||
*/
|
||||
const getDaysLeftColor = (daysLeft: number): string => {
|
||||
if (daysLeft <= 7) return 'var(--n-error-color)'
|
||||
if (daysLeft <= 30) return 'var(--n-warning-color)'
|
||||
return 'var(--n-success-color)'
|
||||
if (daysLeft <= 7) return 'var(--n-error-primary-color)'
|
||||
if (daysLeft <= 30) return 'var(--n-warning-primary-color)'
|
||||
return 'var(--n-success-primary-color)'
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一的状态主题方案(success / warning / error)
|
||||
*/
|
||||
const getScheme = (type: 'success' | 'warning' | 'error') => {
|
||||
if (type === 'success') {
|
||||
return {
|
||||
iconBgClass: 'bg-[#D8FFE4]',
|
||||
style: {
|
||||
backgroundColor: 'var(--n-success-bg-color-light)',
|
||||
color: 'var(--n-success-primary-color)',
|
||||
borderColor: 'var(--n-success-border-color)',
|
||||
},
|
||||
labelStyle: { color: 'var(--n-success-text-color)' },
|
||||
}
|
||||
}
|
||||
if (type === 'warning') {
|
||||
return {
|
||||
iconBgClass: 'bg-[#fff4e2]',
|
||||
style: {
|
||||
backgroundColor: 'var(--n-warning-bg-color-light)',
|
||||
color: 'var(--n-warning-primary-color)',
|
||||
borderColor: 'var(--n-warning-primary-color)',
|
||||
},
|
||||
labelStyle: { color: 'var(--n-warning-primary-color)' },
|
||||
}
|
||||
}
|
||||
return {
|
||||
iconBgClass: 'bg-[#FFE7E7]',
|
||||
style: {
|
||||
backgroundColor: 'var(--n-error-bg-color-light)',
|
||||
color: 'var(--n-error-primary-color)',
|
||||
borderColor: 'var(--n-error-border-color)',
|
||||
},
|
||||
labelStyle: { color: 'var(--n-error-text-color)' },
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const CoreStatusCard = ref([
|
||||
{
|
||||
label: '当前状态',
|
||||
icon: <CheckCircleOutlined />,
|
||||
class: 'border',
|
||||
getThemeType: () => (detailData.value?.valid === 1 ? 'success' : 'error'),
|
||||
render: () => detailData.value ? (
|
||||
<span>
|
||||
{getValidStatus(detailData.value.valid).text}
|
||||
</span>
|
||||
) : '-'
|
||||
},
|
||||
{
|
||||
label: '剩余天数',
|
||||
icon: <ClockCircleOutlined />,
|
||||
class: 'border',
|
||||
getThemeType: () => {
|
||||
const d = detailData.value?.days_left ?? 0
|
||||
if (d <= 7) return 'error'
|
||||
if (d <= 30) return 'warning'
|
||||
return 'success'
|
||||
},
|
||||
render: () => detailData.value ? (
|
||||
<span style={{ color: getDaysLeftColor(detailData.value.days_left) }}>
|
||||
{detailData.value.days_left} 天
|
||||
</span>
|
||||
) : '-'
|
||||
},
|
||||
{
|
||||
label: '错误次数',
|
||||
iconBgClass: 'bg-[#fff4e2] text-[#FF9D00]',
|
||||
icon: <CloseCircleOutlined />,
|
||||
render: () => `${detailData.value?.err_count || 0} 次`
|
||||
},
|
||||
{
|
||||
label: '协议类型',
|
||||
iconBgClass: 'bg-[#E6F1FF] text-[#3B82F6]',
|
||||
icon: <GlobalOutlined />,
|
||||
render: () => detailData.value?.monitor_type || '-',
|
||||
uppercase: true
|
||||
},
|
||||
])
|
||||
|
||||
|
||||
const ValidPeriodCard = ref([
|
||||
{
|
||||
label: '生效时间',
|
||||
labelColor: '#67C23A',
|
||||
render: () => formatDateTime(detailData.value?.not_before || ''),
|
||||
class: 'font-mono',
|
||||
},
|
||||
{
|
||||
label: '到期时间',
|
||||
labelColor: '#FF9D00',
|
||||
render: () => formatDateTime(detailData.value?.not_after || ''),
|
||||
class: 'font-mono',
|
||||
},
|
||||
{
|
||||
label: '距离到期',
|
||||
labelColor: '#EF4444',
|
||||
render: () => detailData.value ? (
|
||||
<span>
|
||||
{detailData.value.days_left} 天
|
||||
</span>
|
||||
) : '-',
|
||||
class: '',
|
||||
},
|
||||
{
|
||||
label: '证书有效期范围',
|
||||
labelColor: '#3B82F6',
|
||||
class: 'font-mono whitespace-nowrap',
|
||||
render: () => formatValidityPeriod(
|
||||
detailData.value?.not_before || '',
|
||||
detailData.value?.not_after || '',
|
||||
),
|
||||
},
|
||||
])
|
||||
/**
|
||||
* 递归渲染证书链节点
|
||||
*/
|
||||
const renderCertChainNode = (node: CertChainNode, level: number = 0, index: number = 0) => {
|
||||
const elements = []
|
||||
|
||||
// 确定证书类型和样式
|
||||
const getCertTypeInfo = (level: number, hasChildren: boolean) => {
|
||||
@@ -258,50 +377,61 @@ export default defineComponent({
|
||||
return {
|
||||
label: '终端证书',
|
||||
color: 'bg-green-500',
|
||||
textColor: 'text-green-700 dark:text-green-400',
|
||||
border: 'border-green-500',
|
||||
lineColorHex: '#22c55e',
|
||||
}
|
||||
} else if (hasChildren) {
|
||||
return {
|
||||
label: `中间证书 #${index + 1}`,
|
||||
color: 'bg-blue-500',
|
||||
textColor: 'text-blue-700 dark:text-blue-400',
|
||||
border: 'border-blue-500',
|
||||
lineColorHex: '#3b82f6',
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
label: '根证书',
|
||||
color: 'bg-purple-500',
|
||||
textColor: 'text-purple-700 dark:text-purple-400',
|
||||
border: 'border-purple-500',
|
||||
lineColorHex: '#a855f7',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const typeInfo = getCertTypeInfo(level, Boolean(node.children && node.children.length > 0))
|
||||
const hasChildren = Boolean(node.children && node.children.length > 0)
|
||||
const typeInfo = getCertTypeInfo(level, hasChildren)
|
||||
|
||||
// 渲染当前节点
|
||||
elements.push(
|
||||
<div
|
||||
key={`cert-${level}-${index}`}
|
||||
class="flex items-center space-x-3 p-2 bg-white dark:bg-gray-800 rounded-lg shadow-sm"
|
||||
style={{ marginLeft: `${level * 1.2}rem` }}
|
||||
>
|
||||
<div class={`w-2.5 h-2.5 ${typeInfo.color} rounded-full flex-shrink-0 shadow-sm`}></div>
|
||||
<div class="flex-1">
|
||||
<span class={`text-[1.2rem] sm:text-[1.3rem] font-medium ${typeInfo.textColor}`}>{typeInfo.label}</span>
|
||||
<div class="text-[1.1rem] sm:text-[1.2rem] text-gray-600 dark:text-gray-400 font-mono mt-1 break-words">
|
||||
{node.common_name}
|
||||
return (
|
||||
<div key={`cert-wrap-${level}-${index}`} class="relative" style={{ paddingLeft: `${level * 2}rem` }}>
|
||||
{hasChildren && (
|
||||
<div
|
||||
class="absolute"
|
||||
style={{
|
||||
top: '36px',
|
||||
bottom: 0,
|
||||
left: `calc(${level * 2}rem + 9px)`, // 对齐圆点中心(增大每级间距)
|
||||
width: '2px',
|
||||
backgroundColor: (typeInfo as any).lineColorHex ?? '#3b82f6',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 行内容 */}
|
||||
<div class="flex items-center space-x-6 p-2">
|
||||
<div class={`w-4 h-4 rounded-full flex-shrink-0 border-2 ${typeInfo.border}`}></div>
|
||||
<div class="flex-1">
|
||||
<span class={`text-[1.4rem] font-bold`}>{typeInfo.label}</span>
|
||||
<div class="font-mono break-words text-color3">
|
||||
{node.common_name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
|
||||
{/* 子节点 */}
|
||||
{hasChildren && node.children!.map((child: CertChainNode, childIndex: number) => (
|
||||
renderCertChainNode(child, level + 1, childIndex)
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
// 递归渲染子节点
|
||||
if (node.children && node.children.length > 0) {
|
||||
node.children.forEach((child: CertChainNode, childIndex: number) => {
|
||||
elements.push(...renderCertChainNode(child, level + 1, childIndex))
|
||||
})
|
||||
}
|
||||
|
||||
return elements
|
||||
}
|
||||
|
||||
// 组件挂载时获取数据
|
||||
@@ -319,7 +449,7 @@ export default defineComponent({
|
||||
size="medium"
|
||||
type="default"
|
||||
onClick={goBack}
|
||||
class="text-[1.3rem] sm:text-[1.4rem]"
|
||||
class="text-[1.3rem] sm:text-[1.4rem] gradient-default-btn"
|
||||
renderIcon={() => (
|
||||
<NIcon>
|
||||
<ArrowLeft />
|
||||
@@ -329,118 +459,134 @@ export default defineComponent({
|
||||
返回监控列表
|
||||
</NButton>
|
||||
</NSpace>
|
||||
<h1 class="text-[1.8rem] sm:text-[2rem] lg:text-[2.2rem] font-semibold text-gray-800 dark:text-gray-200 break-words leading-tight">
|
||||
{detailData.value?.name || '监控详情'} - 证书详情
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* 内容区域 */}
|
||||
{detailData.value ? (
|
||||
<div class="space-y-4 sm:space-y-5 lg:space-y-6">
|
||||
{/* 合并的监控和证书详情模块 */}
|
||||
<NCard
|
||||
title="监控详情与证书信息"
|
||||
class="[&_.n-card-header_.n-card-header__main]:text-[1.5rem] [&_.n-card-header_.n-card-header__main]:font-medium"
|
||||
bordered
|
||||
>
|
||||
{{
|
||||
'header-extra': () => (
|
||||
<NIcon size="24" color="var(--n-primary-color)">
|
||||
<Information />
|
||||
</NIcon>
|
||||
),
|
||||
default: () => (
|
||||
<div class="space-y-6">
|
||||
{/* 核心状态信息 - 最重要 */}
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4 text-primary text-[1.6rem] sm:text-[1.7rem] border-b-2 border-primary/20 pb-2">
|
||||
核心状态
|
||||
</h4>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div class="bg-gradient-to-br from-blue-50 to-blue-100 dark:from-blue-900/30 dark:to-blue-800/30 p-4 rounded-xl border border-blue-200 dark:border-blue-700">
|
||||
<NText
|
||||
depth="3"
|
||||
class="text-[1.3rem] sm:text-[1.4rem] font-medium text-blue-700 dark:text-blue-300"
|
||||
>
|
||||
当前状态
|
||||
</NText>
|
||||
<div class="mt-2 font-bold text-[1.5rem] sm:text-[1.6rem]">
|
||||
{detailData.value && (
|
||||
<span style={{ color: getValidStatus(detailData.value.valid).color }}>
|
||||
{getValidStatus(detailData.value.valid).text}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gradient-to-br from-green-50 to-green-100 dark:from-green-900/30 dark:to-green-800/30 p-4 rounded-xl border border-green-200 dark:border-green-700">
|
||||
<NText
|
||||
depth="3"
|
||||
class="text-[1.3rem] sm:text-[1.4rem] font-medium text-green-700 dark:text-green-300"
|
||||
>
|
||||
剩余天数
|
||||
</NText>
|
||||
<div class="mt-2 font-bold text-[1.5rem] sm:text-[1.6rem]">
|
||||
{detailData.value && (
|
||||
<span style={{ color: getDaysLeftColor(detailData.value.days_left) }}>
|
||||
{detailData.value.days_left} 天
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gradient-to-br from-purple-50 to-purple-100 dark:from-purple-900/30 dark:to-purple-800/30 p-4 rounded-xl border border-purple-200 dark:border-purple-700">
|
||||
<NText
|
||||
depth="3"
|
||||
class="text-[1.3rem] sm:text-[1.4rem] font-medium text-purple-700 dark:text-purple-300"
|
||||
>
|
||||
错误次数
|
||||
</NText>
|
||||
<div class="mt-2 font-bold text-[1.5rem] sm:text-[1.6rem]">
|
||||
<span
|
||||
style={{
|
||||
color:
|
||||
(detailData.value?.err_count || 0) > 0
|
||||
? 'var(--n-error-color)'
|
||||
: 'var(--n-success-color)',
|
||||
}}
|
||||
>
|
||||
{detailData.value?.err_count || 0} 次
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gradient-to-br from-orange-50 to-orange-100 dark:from-orange-900/30 dark:to-orange-800/30 p-4 rounded-xl border border-orange-200 dark:border-orange-700">
|
||||
<NText
|
||||
depth="3"
|
||||
class="text-[1.3rem] sm:text-[1.4rem] font-medium text-orange-700 dark:text-orange-300"
|
||||
>
|
||||
协议类型
|
||||
</NText>
|
||||
<div class="mt-2 font-bold text-[1.5rem] sm:text-[1.6rem] uppercase">
|
||||
{detailData.value?.monitor_type || '-'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 内容区域 */}
|
||||
{detailData.value ? (
|
||||
<div class="space-y-4 sm:space-y-5 lg:space-y-6">
|
||||
{/* 合并的监控和证书详情模块 */}
|
||||
<NCard
|
||||
title={() => (
|
||||
<h1 class="text-[1.8rem] sm:text-[1.8rem] lg:text-[1.8rem] font-semibold break-words leading-tight">
|
||||
{detailData.value?.name || "监控详情"} - 证书详情
|
||||
</h1>
|
||||
)}
|
||||
class="[&_.n-card-header_.n-card-header__main]:text-[1.5rem] [&_.n-card-header_.n-card-header__main]:font-medium !bg-[var(--content-bg-base)]"
|
||||
bordered
|
||||
>
|
||||
{{
|
||||
"header-extra": () => (
|
||||
<span class={styles.headerIcon}>
|
||||
<NIcon size="24">
|
||||
<Information />
|
||||
</NIcon>
|
||||
</span>
|
||||
),
|
||||
default: () => (
|
||||
<div class="space-y-6">
|
||||
{/* 核心状态信息 - 最重要 */}
|
||||
<div class="pb-6">
|
||||
<h4 class="font-semibold mb-6 text-primary text-[1.6rem] sm:text-[1.7rem] border-b border-[var(--n-border-color)] pb-4">
|
||||
核心状态
|
||||
</h4>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{CoreStatusCard.value.map((card, index) => {
|
||||
const theme: any = card.getThemeType
|
||||
? getScheme(card.getThemeType())
|
||||
: {
|
||||
iconBgClass: (card as any).iconBgClass || "",
|
||||
style: (card as any).style || {},
|
||||
labelStyle: (card as any).labelStyle || {},
|
||||
};
|
||||
|
||||
// 简化判断:只对前两个元素应用样式类
|
||||
const shouldApplyStyles = index < 2;
|
||||
const themeType = card.getThemeType?.();
|
||||
const isSuccess = shouldApplyStyles && themeType === "success";
|
||||
const isError = shouldApplyStyles && themeType === "error";
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
class={`${styles.coreStatusCard} ${
|
||||
isSuccess ? styles.coreStatusCardSuccess : ""
|
||||
} ${
|
||||
isError ? styles.coreStatusCardError : ""
|
||||
} flex flex-row items-center gap-4 p-6 rounded-xl ${
|
||||
card.class || ""
|
||||
}`}
|
||||
style={theme.style}
|
||||
>
|
||||
<span
|
||||
class={`${styles.coreStatusIcon} ${
|
||||
isSuccess
|
||||
? styles.coreStatusIconSuccess
|
||||
: ""
|
||||
} ${
|
||||
isError
|
||||
? styles.coreStatusIconError
|
||||
: ""
|
||||
} w-[48px] h-[48px] rounded-[6px] overflow-hidden flex items-center justify-center ${
|
||||
theme.iconBgClass || ""
|
||||
}`}
|
||||
>
|
||||
<NIcon size="36">{card.icon}</NIcon>
|
||||
</span>
|
||||
<div>
|
||||
<div
|
||||
class={`font-bold text-[1.5rem] sm:text-[1.6rem] ${
|
||||
card.uppercase ? "uppercase" : ""
|
||||
} ${
|
||||
isError ? styles.coreStatusTextError : ""
|
||||
}`}
|
||||
>
|
||||
{card.render()}
|
||||
</div>
|
||||
<span
|
||||
class={`text-[1.2rem] sm:text-[1.4rem] font-medium text-color5 ${
|
||||
isSuccess
|
||||
? styles.coreStatusLabelSuccess
|
||||
: ""
|
||||
} ${
|
||||
isError
|
||||
? styles.coreStatusLabelError
|
||||
: ""
|
||||
}`}
|
||||
style={{
|
||||
...theme.labelStyle,
|
||||
...(isDark.value && index === 2 && { color: "#FF9D00" }),
|
||||
...(isDark.value && index === 3 && { color: "#3B82F6" }),
|
||||
}}
|
||||
>
|
||||
{card.label}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 监控配置信息 */}
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4 text-primary text-[1.6rem] sm:text-[1.7rem] border-b-2 border-primary/20 pb-2">
|
||||
<div class="pb-6">
|
||||
<h4 class="font-semibold mb-6 text-primary text-[1.6rem] sm:text-[1.7rem] border-b border-[var(--n-border-color)] pb-4">
|
||||
监控配置
|
||||
</h4>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div class="space-y-4">
|
||||
<div class="bg-gray-50 dark:bg-gray-800/50 p-4 rounded-lg">
|
||||
<NText depth="3" class="text-[1.3rem] sm:text-[1.4rem] font-medium">
|
||||
<div class="p-4 rounded-lg">
|
||||
<NText depth="3" class="text-[1.4rem] font-medium">
|
||||
监控名称
|
||||
</NText>
|
||||
<div class="mt-2 font-medium text-[1.4rem] sm:text-[1.5rem] break-words">
|
||||
<div class="mt-2 font-[600] text-[1.4rem] sm:text-[1.5rem] break-words">
|
||||
{detailData.value?.name || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 dark:bg-gray-800/50 p-4 rounded-lg">
|
||||
<NText depth="3" class="text-[1.3rem] sm:text-[1.4rem] font-medium">
|
||||
<div class="p-4 rounded-lg">
|
||||
<NText depth="3" class="text-[1.4rem] font-medium">
|
||||
监控目标
|
||||
</NText>
|
||||
<div class="mt-2 font-medium text-[1.4rem] sm:text-[1.5rem]">
|
||||
<div class="mt-2 font-[600] text-[1.4rem] sm:text-[1.5rem]">
|
||||
<a
|
||||
href={`https://${detailData.value?.target}`}
|
||||
target="_blank"
|
||||
@@ -453,19 +599,19 @@ export default defineComponent({
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div class="bg-gray-50 dark:bg-gray-800/50 p-4 rounded-lg">
|
||||
<NText depth="3" class="text-[1.3rem] sm:text-[1.4rem] font-medium">
|
||||
<div class="p-4 rounded-lg">
|
||||
<NText depth="3" class="text-[1.4rem] font-medium">
|
||||
证书颁发机构
|
||||
</NText>
|
||||
<div class="mt-2 font-medium text-[1.4rem] sm:text-[1.5rem]">
|
||||
<div class="mt-2 font-[600] text-[1.4rem] sm:text-[1.5rem]">
|
||||
{detailData.value?.ca || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 dark:bg-gray-800/50 p-4 rounded-lg">
|
||||
<NText depth="3" class="text-[1.3rem] sm:text-[1.4rem] font-medium">
|
||||
<div class="p-4 rounded-lg">
|
||||
<NText depth="3" class="text-[1.4rem] font-medium">
|
||||
上次检测时间
|
||||
</NText>
|
||||
<div class="mt-2 font-medium text-[1.4rem] sm:text-[1.5rem] break-words">
|
||||
<div class="mt-2 font-[600] text-[1.4rem] sm:text-[1.5rem] break-words">
|
||||
{formatDateTime(detailData.value?.last_time || '')}
|
||||
</div>
|
||||
</div>
|
||||
@@ -474,7 +620,7 @@ export default defineComponent({
|
||||
<NText depth="3" class="text-[1.3rem] sm:text-[1.4rem] font-medium">
|
||||
支持的TLS版本
|
||||
</NText>
|
||||
<div class="mt-2 font-medium text-[1.4rem] sm:text-[1.5rem]">
|
||||
<div class="mt-2 font-[600] text-[1.4rem] sm:text-[1.5rem]">
|
||||
{detailData.value.tls_version}
|
||||
</div>
|
||||
</div>
|
||||
@@ -484,30 +630,30 @@ export default defineComponent({
|
||||
</div>
|
||||
|
||||
{/* 证书基本信息 */}
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4 text-success text-[1.6rem] sm:text-[1.7rem] border-b-2 border-success/20 pb-2">
|
||||
<div class="pb-6">
|
||||
<h4 class="font-semibold mb-6 text-success text-[1.6rem] sm:text-[1.7rem] border-b border-[var(--n-border-color)] pb-4">
|
||||
证书基本信息
|
||||
</h4>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div class="bg-green-50 dark:bg-green-900/20 p-4 rounded-xl border border-green-200 dark:border-green-800">
|
||||
<div>
|
||||
<NText
|
||||
depth="3"
|
||||
class="text-[1.3rem] sm:text-[1.4rem] font-medium text-green-700 dark:text-green-400"
|
||||
class="text-[1.4rem] font-medium"
|
||||
>
|
||||
通用名称 (CN)
|
||||
</NText>
|
||||
<div class="mt-2 font-mono text-[1.4rem] sm:text-[1.5rem] text-green-800 dark:text-green-300 break-all leading-relaxed">
|
||||
<div class="mt-2 font-mono font-[600] text-[1.4rem] sm:text-[1.5rem] break-all leading-relaxed">
|
||||
{detailData.value?.common_name || '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-4 rounded-xl border border-blue-200 dark:border-blue-800">
|
||||
<div>
|
||||
<NText
|
||||
depth="3"
|
||||
class="text-[1.3rem] sm:text-[1.4rem] font-medium text-blue-700 dark:text-blue-400"
|
||||
class="text-[1.4rem] font-medium"
|
||||
>
|
||||
主题备用名称 (SAN)
|
||||
</NText>
|
||||
<div class="mt-2 font-mono text-[1.4rem] sm:text-[1.5rem] text-blue-800 dark:text-blue-300 break-all leading-relaxed">
|
||||
<div class="mt-2 font-mono font-[600] text-[1.4rem] sm:text-[1.5rem] break-all leading-relaxed">
|
||||
{detailData.value?.sans || '-'}
|
||||
</div>
|
||||
</div>
|
||||
@@ -515,69 +661,38 @@ export default defineComponent({
|
||||
</div>
|
||||
|
||||
{/* 有效期详情 */}
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4 text-success text-[1.6rem] sm:text-[1.7rem] border-b-2 border-success/20 pb-2">
|
||||
<div class="pb-6">
|
||||
<h4 class="font-semibold mb-6 text-success text-[1.6rem] sm:text-[1.7rem] border-b border-[var(--n-border-color)] pb-4">
|
||||
有效期详情
|
||||
</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div class="bg-gradient-to-br from-green-50 to-emerald-50 dark:from-green-900/30 dark:to-emerald-900/30 p-4 rounded-xl border border-green-200 dark:border-green-700">
|
||||
<NText
|
||||
depth="3"
|
||||
class="text-[1.3rem] sm:text-[1.4rem] font-medium text-green-700 dark:text-green-300"
|
||||
<div class="flex flex-wrap gap-4">
|
||||
{ValidPeriodCard.value.map((card, index) => (
|
||||
<div
|
||||
key={index}
|
||||
class={`border-l-[6px] pl-4 min-w-0`}
|
||||
style={{
|
||||
borderLeftColor: card.labelColor,
|
||||
flex: index === 3 ? '1 1 auto' : '1 0 auto',
|
||||
}}
|
||||
>
|
||||
生效时间
|
||||
</NText>
|
||||
<div class="mt-2 font-mono text-[1.4rem] sm:text-[1.5rem] text-green-600 dark:text-green-400 break-words">
|
||||
{formatDateTime(detailData.value?.not_before || '')}
|
||||
<div class={`font-semibold text-[1.6rem] ${card.class || ''}`}>
|
||||
{card.render()}
|
||||
</div>
|
||||
<NText depth="3" class="mt-2 text-[1.2rem]">
|
||||
{card.label}
|
||||
</NText>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gradient-to-br from-orange-50 to-red-50 dark:from-orange-900/30 dark:to-red-900/30 p-4 rounded-xl border border-orange-200 dark:border-orange-700">
|
||||
<NText
|
||||
depth="3"
|
||||
class="text-[1.3rem] sm:text-[1.4rem] font-medium text-orange-700 dark:text-orange-300"
|
||||
>
|
||||
到期时间
|
||||
</NText>
|
||||
<div class="mt-2 font-mono text-[1.4rem] sm:text-[1.5rem] text-orange-600 dark:text-orange-400 break-words">
|
||||
{formatDateTime(detailData.value?.not_after || '')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gradient-to-br from-purple-50 to-indigo-50 dark:from-purple-900/30 dark:to-indigo-900/30 p-4 rounded-xl border border-purple-200 dark:border-purple-700 md:col-span-2 lg:col-span-1">
|
||||
<NText
|
||||
depth="3"
|
||||
class="text-[1.3rem] sm:text-[1.4rem] font-medium text-purple-700 dark:text-purple-300"
|
||||
>
|
||||
距离到期
|
||||
</NText>
|
||||
<div class="mt-2 font-bold text-[1.5rem] sm:text-[1.6rem]">
|
||||
{detailData.value && (
|
||||
<span style={{ color: getDaysLeftColor(detailData.value.days_left) }}>
|
||||
{detailData.value.days_left} 天
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 bg-gray-50 dark:bg-gray-800/50 p-4 rounded-lg">
|
||||
<NText depth="3" class="text-[1.3rem] sm:text-[1.4rem] font-medium">
|
||||
证书有效期范围
|
||||
</NText>
|
||||
<div class="mt-2 font-medium text-[1.4rem] sm:text-[1.5rem] break-words">
|
||||
{formatValidityPeriod(
|
||||
detailData.value?.not_before || '',
|
||||
detailData.value?.not_after || '',
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 证书链路信息 - 视觉增强 */}
|
||||
{detailData.value?.cert_chain && (
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4 text-success text-[1.6rem] sm:text-[1.7rem] border-b-2 border-success/20 pb-2">
|
||||
<h4 class="font-semibold mb-6 text-success text-[1.6rem] sm:text-[1.7rem] border-b border-[var(--n-border-color)] pb-4">
|
||||
证书链路信息
|
||||
</h4>
|
||||
<div class="bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 p-5 rounded-xl border border-blue-200 dark:border-blue-800">
|
||||
<div class="">
|
||||
<div class="space-y-4">{renderCertChainNode(detailData.value.cert_chain)}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -586,11 +701,11 @@ export default defineComponent({
|
||||
{/* 验证错误信息 */}
|
||||
{detailData.value?.verify_error && (
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4 text-error text-[1.6rem] sm:text-[1.7rem] border-b-2 border-error/20 pb-2">
|
||||
<h4 class="font-semibold mb-6 text-error text-[1.6rem] border-[var(--n-border-color)] border-b border-error/20 pb-4">
|
||||
验证错误信息
|
||||
</h4>
|
||||
<div class="bg-red-50 dark:bg-red-900/20 p-4 rounded-xl border border-red-200 dark:border-red-800">
|
||||
<div class="text-red-600 dark:text-red-300 text-[1.4rem] sm:text-[1.5rem] leading-relaxed break-words">
|
||||
<div class="">
|
||||
<div class="text-red-600 leading-relaxed break-words">
|
||||
{detailData.value.verify_error}
|
||||
</div>
|
||||
</div>
|
||||
@@ -606,7 +721,7 @@ export default defineComponent({
|
||||
</div>
|
||||
) : (
|
||||
!loading.value && (
|
||||
<NCard bordered class="text-center [&_.n-empty__description]:text-[1.4rem]">
|
||||
<NCard bordered class="text-center [&_.n-empty__description]:text-[1.4rem] !bg-[var(--content-bg-base)]">
|
||||
<NEmpty description="未找到监控详情数据" size="large">
|
||||
{{
|
||||
extra: () => (
|
||||
|
||||
@@ -19,7 +19,6 @@ export default defineComponent({
|
||||
// 消息提示和错误处理
|
||||
const message = useMessage()
|
||||
const { handleError } = useError()
|
||||
|
||||
// 当前激活的标签页
|
||||
const activeTab = ref<'import' | 'template'>('import')
|
||||
|
||||
@@ -246,7 +245,7 @@ export default defineComponent({
|
||||
{uploadStatus.value.success ? <CheckmarkCircleOutline /> : <CloudUploadOutline />}
|
||||
</NIcon>
|
||||
<NText class="text-lg block mb-2">{uploadTipText.value}</NText>
|
||||
<NText depth="3" class="text-sm">
|
||||
<NText depth="3" class="text-lg">
|
||||
{selectedFile.value ? '点击重新选择文件或拖拽新文件到此区域' : $t('t_13_1752724148548')}
|
||||
</NText>
|
||||
</div>
|
||||
@@ -256,10 +255,10 @@ export default defineComponent({
|
||||
{/* 上传进度条 */}
|
||||
{uploadStatus.value.uploading && (
|
||||
<div class="mt-4">
|
||||
<NText class="text-sm text-gray-500 mb-2">上传进度: {uploadStatus.value.progress}%</NText>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2">
|
||||
<NText class="text-xl text-color5 mb-2">上传进度: {uploadStatus.value.progress}%</NText>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 mt-2">
|
||||
<div
|
||||
class="bg-primary h-2 rounded-full transition-all duration-300"
|
||||
class="monitor-upload-progress-bar bg-[var(--monitor-upload-progress-bar-bg)] h-2 rounded-full transition-all duration-300"
|
||||
style={{ width: `${uploadStatus.value.progress}%` }}
|
||||
></div>
|
||||
</div>
|
||||
@@ -280,6 +279,7 @@ export default defineComponent({
|
||||
)}
|
||||
<NButton
|
||||
type="primary"
|
||||
class="gradient-primary-btn"
|
||||
size="large"
|
||||
loading={uploadStatus.value.uploading}
|
||||
disabled={!canUpload.value}
|
||||
@@ -325,7 +325,7 @@ export default defineComponent({
|
||||
|
||||
<NSpace vertical size="large">
|
||||
{supportedFormats.map((format) => (
|
||||
<div key={format} class="flex items-center justify-between p-4 border border-gray-200 rounded-lg">
|
||||
<div key={format} class="flex items-center justify-between p-4 border border-[var(--border-color-transparent)] bg-[var(--content-bg-secondary)] rounded-lg">
|
||||
<div class="flex items-center">
|
||||
<NIcon size={24} class="mr-3 text-primary">
|
||||
<DocumentOutline />
|
||||
@@ -334,7 +334,7 @@ export default defineComponent({
|
||||
<NText strong class="block">
|
||||
{format.toUpperCase()} 模板
|
||||
</NText>
|
||||
<NText depth="3" class="text-sm">
|
||||
<NText depth="3" class="text-lg">
|
||||
适用于 {format === 'xlsx' ? 'Excel' : format.toUpperCase()} 格式导入
|
||||
</NText>
|
||||
</div>
|
||||
@@ -342,6 +342,7 @@ export default defineComponent({
|
||||
<NButton
|
||||
type="primary"
|
||||
size="small"
|
||||
class="gradient-primary-btn"
|
||||
onClick={() => handleDownloadTemplate(format)}
|
||||
v-slots={{
|
||||
icon: () => (
|
||||
|
||||
@@ -54,10 +54,10 @@ export default defineComponent({
|
||||
// 头部左侧区域 - 添加按钮和导入按钮
|
||||
headerLeft: () => (
|
||||
<NSpace>
|
||||
<NButton type="primary" size="large" class="px-5" onClick={openAddForm}>
|
||||
<NButton type="primary" size="large" class="gradient-primary-btn px-5" onClick={openAddForm}>
|
||||
{$t('t_11_1745289354516')}
|
||||
</NButton>
|
||||
<NButton type="default" size="large" class="px-5" onClick={openImportForm}>
|
||||
<NButton type="default" size="large" class="gradient-default-btn px-5" onClick={openImportForm}>
|
||||
{$t('t_0_1752724141380')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
@@ -65,7 +65,7 @@ export default defineComponent({
|
||||
// 头部右侧区域 - 搜索框和列设置
|
||||
headerRight: () => (
|
||||
<NSpace align="center" size="medium">
|
||||
<SearchComponent placeholder={$t('t_12_1745289356974')} />
|
||||
<SearchComponent class="header-search" placeholder={$t('t_12_1745289356974')} />
|
||||
<ColumnSettingsComponent />
|
||||
</NSpace>
|
||||
),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { computed, onMounted, onUnmounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { FormRules, NButton, NSpace, NSwitch, NText, NDivider, type DataTableColumns } from 'naive-ui'
|
||||
import { useTheme } from "@baota/naive-ui/theme";
|
||||
|
||||
// 钩子和工具
|
||||
import {
|
||||
@@ -72,6 +73,7 @@ const {
|
||||
// 错误处理
|
||||
const { handleError } = useError()
|
||||
|
||||
const { isDark } = useTheme()
|
||||
/**
|
||||
* 验证域名(或IP)+端口格式
|
||||
* @param value - 要验证的值
|
||||
@@ -130,7 +132,7 @@ export const useController = (): MonitorControllerExposes => {
|
||||
href={`https://${row.target}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style="color: var(--primary-color); text-decoration: none;"
|
||||
style="color: var(--form-more-color); text-decoration: var(--table-link-type);"
|
||||
>
|
||||
{row.target}
|
||||
</a>
|
||||
@@ -218,7 +220,7 @@ export const useController = (): MonitorControllerExposes => {
|
||||
return <span style="color: var(--n-text-color-disabled); font-size: 12px;">-</span>
|
||||
}
|
||||
|
||||
return <TypeIcon icon={reportTypes} />
|
||||
return <TypeIcon icon={reportTypes} class={isDark.value ? 'rounded-full' : ''} />
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -242,18 +244,39 @@ export const useController = (): MonitorControllerExposes => {
|
||||
align: 'right',
|
||||
render: (row: SiteMonitorItem) => {
|
||||
return (
|
||||
<NSpace justify="end">
|
||||
<NButton size="tiny" strong secondary type="info" onClick={() => openDetailPage(row)}>
|
||||
详情
|
||||
</NButton>
|
||||
<NButton size="tiny" strong secondary type="primary" onClick={() => openEditForm(row)}>
|
||||
{$t('t_11_1745215915429')}
|
||||
</NButton>
|
||||
<NButton size="tiny" strong secondary type="error" onClick={() => confirmDelete(row)}>
|
||||
{$t('t_12_1745215914312')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
)
|
||||
<NSpace justify="end">
|
||||
<NButton
|
||||
size="tiny"
|
||||
strong
|
||||
secondary
|
||||
type="info"
|
||||
class="table-action-btn"
|
||||
onClick={() => openDetailPage(row)}
|
||||
>
|
||||
详情
|
||||
</NButton>
|
||||
<NButton
|
||||
size="tiny"
|
||||
strong
|
||||
secondary
|
||||
type="primary"
|
||||
class="table-action-btn"
|
||||
onClick={() => openEditForm(row)}
|
||||
>
|
||||
{$t("t_11_1745215915429")}
|
||||
</NButton>
|
||||
<NButton
|
||||
size="tiny"
|
||||
strong
|
||||
secondary
|
||||
type="error"
|
||||
class="table-action-btn-danger"
|
||||
onClick={() => confirmDelete(row)}
|
||||
>
|
||||
{$t("t_12_1745215914312")}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
);
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -431,7 +454,11 @@ export const useMonitorFormController = (data: UpdateSiteMonitorParams | null =
|
||||
* @returns 分组标题配置
|
||||
*/
|
||||
const createGroupTitle = (title: string) => {
|
||||
return useFormCustom(() => <NDivider style="margin: 12px 0 8px 0; font-weight: 500;">{title}</NDivider>)
|
||||
return useFormCustom(() => (
|
||||
<NDivider style="margin: 12px 0 8px 0;">
|
||||
<span class="font-[var(--form-divider-title-weight)]">{title}</span>
|
||||
</NDivider>
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,7 +34,7 @@ export default defineComponent({
|
||||
<NButton
|
||||
type="primary"
|
||||
size="large"
|
||||
class="px-5"
|
||||
class="gradient-primary-btn px-5"
|
||||
onClick={openCreateLeafCertModal}
|
||||
>
|
||||
签发证书
|
||||
@@ -57,6 +57,7 @@ export default defineComponent({
|
||||
onUpdateValue={handleCaIdChange}
|
||||
/>
|
||||
<SearchComponent
|
||||
class="header-search"
|
||||
placeholder="请输入名称搜索"
|
||||
style={{ width: "240px" }}
|
||||
/>
|
||||
|
||||
@@ -94,8 +94,8 @@ export const useController = () => {
|
||||
|
||||
return (
|
||||
<div class="flex flex-col">
|
||||
<div class="text-gray-900">{row.cn}</div>
|
||||
{sanText && <div class="text-xl text-gray-500">SAN: {sanText}</div>}
|
||||
<div>{row.cn}</div>
|
||||
{sanText && <div class="text-xl text-color5">SAN: {sanText}</div>}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@@ -165,12 +165,12 @@ export const useController = () => {
|
||||
textColor = "text-orange-500";
|
||||
} else {
|
||||
remainingText = `已过期 ${Math.abs(remainingDays)} 天`;
|
||||
textColor = "text-red-500";
|
||||
textColor = "text-[var(--n-error-primary-color)]";
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="flex flex-col">
|
||||
<div class="text-gray-900">{row.not_after}</div>
|
||||
<div>{row.not_after}</div>
|
||||
<div class={`text-xl ${textColor}`}>{remainingText}</div>
|
||||
</div>
|
||||
);
|
||||
@@ -207,6 +207,7 @@ export const useController = () => {
|
||||
strong
|
||||
secondary
|
||||
type="primary"
|
||||
class="table-action-btn"
|
||||
onClick={() => handleDownload(row)}
|
||||
>
|
||||
下载
|
||||
@@ -216,6 +217,7 @@ export const useController = () => {
|
||||
strong
|
||||
secondary
|
||||
type="error"
|
||||
class="table-action-btn-danger"
|
||||
onClick={() => handleDelete(row)}
|
||||
>
|
||||
删除
|
||||
@@ -699,7 +701,8 @@ export const useCreateLeafCertController = (list: IntermediateCa[]) => {
|
||||
style={{ width: "160px" }}
|
||||
/>
|
||||
<NButton
|
||||
type="primary"
|
||||
type="primary"
|
||||
class="gradient-primary-btn"
|
||||
onClick={addSanInput}
|
||||
disabled={!sanInputValue.value.trim()}
|
||||
>
|
||||
|
||||
@@ -253,10 +253,12 @@ export default defineComponent({
|
||||
>
|
||||
<NDivider>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-[#18a058] font-medium">更多配置</span>
|
||||
<span class="text-[var(--form-more-color)] font-medium">
|
||||
更多配置
|
||||
</span>
|
||||
<NIcon
|
||||
size="16"
|
||||
color="#18a058"
|
||||
color="var(--form-more-color)"
|
||||
class="transition-transform duration-200"
|
||||
style={{
|
||||
transform: showAdvancedConfig.value
|
||||
@@ -309,7 +311,11 @@ export default defineComponent({
|
||||
</div>
|
||||
<div class="flex justify-end gap-3 mt-6">
|
||||
<NButton onClick={handleCancel}>取消</NButton>
|
||||
<NButton type="primary" onClick={handleFormSubmit}>
|
||||
<NButton
|
||||
class="gradient-primary-btn"
|
||||
type="primary"
|
||||
onClick={handleFormSubmit}
|
||||
>
|
||||
确定
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
:global(.n-data-table .n-data-table-th) {
|
||||
padding: 12px 16px;
|
||||
background-color: #f9fafb;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export default defineComponent({
|
||||
show-arrow={false}
|
||||
width={100}
|
||||
>
|
||||
<NButton type="primary" size="large" class="px-5">
|
||||
<NButton type="primary" size="large" class="gradient-primary-btn px-5">
|
||||
创建CA
|
||||
<NIcon size="20" class="ml-2">
|
||||
<ChevronDown />
|
||||
@@ -70,7 +70,7 @@ export default defineComponent({
|
||||
</NButton>
|
||||
</NDropdown>
|
||||
),
|
||||
headerRight: () => <SearchComponent placeholder="请输入名称搜索" />,
|
||||
headerRight: () => <SearchComponent class="header-search" placeholder="请输入名称搜索" />,
|
||||
content: () => (
|
||||
<div class="rounded-lg">
|
||||
<TableComponent
|
||||
|
||||
@@ -63,8 +63,8 @@ export const useController = () => {
|
||||
width: 250,
|
||||
render: (row: PrivateCaItem) => (
|
||||
<div class="flex flex-col">
|
||||
<div class="text-gray-900">{row.name}</div>
|
||||
<div class="text-xl text-gray-500">{row.distinguishedName}</div>
|
||||
<div>{row.name}</div>
|
||||
<div class="text-xl text-[#969696]">{row.distinguishedName}</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@@ -83,8 +83,8 @@ export const useController = () => {
|
||||
width: 120,
|
||||
render: (row: PrivateCaItem) => (
|
||||
<div class="flex flex-col">
|
||||
<div class="text-gray-900">{row.algorithm.toUpperCase()}</div>
|
||||
<div class="text-xl text-gray-500">{row.keySize} bit</div>
|
||||
<div>{row.algorithm.toUpperCase()}</div>
|
||||
<div class="text-xl text-[#969696]">{row.keySize} bit</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@@ -111,19 +111,19 @@ export const useController = () => {
|
||||
textColor = "text-orange-500";
|
||||
} else {
|
||||
remainingText = `${remainingDays} 天后`;
|
||||
textColor = "text-gray-500";
|
||||
textColor = "text-[#969696]";
|
||||
}
|
||||
} else if (remainingDays === 0) {
|
||||
remainingText = "今天到期";
|
||||
textColor = "text-orange-500";
|
||||
} else {
|
||||
remainingText = `已过期 ${Math.abs(remainingDays)} 天`;
|
||||
textColor = "text-red-500";
|
||||
textColor = "text-[var(--n-error-primary-color)]";
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="flex flex-col">
|
||||
<div class="text-gray-900">{row.validTo}</div>
|
||||
<div>{row.validTo}</div>
|
||||
<div class={`text-xl ${textColor}`}>{remainingText}</div>
|
||||
</div>
|
||||
);
|
||||
@@ -156,6 +156,7 @@ export const useController = () => {
|
||||
render: (row: PrivateCaItem) => (
|
||||
<NFlex justify="end">
|
||||
<NButton
|
||||
class="table-action-btn"
|
||||
size="tiny"
|
||||
strong
|
||||
secondary
|
||||
@@ -165,6 +166,7 @@ export const useController = () => {
|
||||
下载
|
||||
</NButton>
|
||||
<NButton
|
||||
class="table-action-btn-danger"
|
||||
size="tiny"
|
||||
strong
|
||||
secondary
|
||||
|
||||
@@ -3,6 +3,7 @@ import { $t } from '@locales/index'
|
||||
import { LogoGithub } from '@vicons/ionicons5'
|
||||
import { getVersion } from '@api/setting'
|
||||
import type { VersionData } from '@/types/setting'
|
||||
import styles from './index.module.css'
|
||||
/**
|
||||
* 关于我们标签页组件
|
||||
*/
|
||||
@@ -42,7 +43,10 @@ export default defineComponent({
|
||||
|
||||
return () => (
|
||||
<div class="about-settings">
|
||||
<NCard title={$t('t_4_1745833932780')} class="mb-4">
|
||||
<div class="mb-4">
|
||||
<div class="flex items-center mb-6 mt-2">
|
||||
<h2 class={`${styles.sectionTitle} ml-2 text-[1.8rem] font-semibold`}>版本信息</h2>
|
||||
</div>
|
||||
<NSpace vertical size={24}>
|
||||
<NDescriptions bordered>
|
||||
<NDescriptionsItem label={$t('t_5_1745833933241')}>
|
||||
@@ -74,22 +78,25 @@ export default defineComponent({
|
||||
</NDescriptionsItem>
|
||||
</NDescriptions>
|
||||
</NSpace>
|
||||
</NCard>
|
||||
</div>
|
||||
|
||||
{/* 新版本信息卡片 */}
|
||||
{hasUpdate.value && versionData.value && (
|
||||
<NCard title="发现新版本" class="mb-4">
|
||||
<div class="mb-4">
|
||||
<div class="flex items-center mb-6 mt-2">
|
||||
<h2 class={`${styles.sectionTitle} ml-2 text-[1.8rem] font-semibold`}>发现新版本</h2>
|
||||
</div>
|
||||
<NAlert type="info" title={`新版本 ${versionData.value.new_version} 已发布`} class="mb-[1.6rem]">
|
||||
<div class="text-[1.4rem]">
|
||||
<div class="mb-[1.2rem] text-[1.4rem]">发布日期: {versionData.value.date}</div>
|
||||
<div class="mb-[1.2rem] text-[1.4rem]">
|
||||
<strong>更新内容:</strong>
|
||||
</div>
|
||||
<div class="whitespace-pre-line text-gray-700 text-[1.3rem] leading-relaxed">
|
||||
<div class="whitespace-pre-line text-color5 text-[1.3rem] leading-relaxed">
|
||||
{versionData.value.log.replace(/\\r\\n/g, '\n').replace(/\\n/g, '\n')}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<NButton size="medium" type="primary" onClick={goToGitHub}>
|
||||
<NButton class="gradient-primary-btn" size="medium" type="primary" onClick={goToGitHub}>
|
||||
<div class="flex items-center">
|
||||
<NIcon size="18" class="mr-2">
|
||||
<LogoGithub />
|
||||
@@ -100,16 +107,19 @@ export default defineComponent({
|
||||
</div>
|
||||
</div>
|
||||
</NAlert>
|
||||
</NCard>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<NCard title={$t('t_13_1745833933630')} class="mb-4">
|
||||
<div class="about-content">
|
||||
<p class="text-gray-700 leading-relaxed">
|
||||
<p class="text-[3rem] font-medium">AllinSSL</p>
|
||||
<div class="mb-4">
|
||||
<div class="flex items-center mb-6 mt-2">
|
||||
<h2 class={`${styles.sectionTitle} ml-2 text-[1.8rem] font-semibold`}>关于产品</h2>
|
||||
</div>
|
||||
<div class="about-content bg-[var(--setting-input-bg)] px-[2rem] py-[2.4rem] rounded-[6px]">
|
||||
<p class="leading-relaxed">
|
||||
<p class="text-[2rem] font-semibold">AllinSSL</p>
|
||||
<br />
|
||||
<p class="text-[1.6rem] text-primary mb-[2rem]">{$t('t_35_1746773362992')}</p>
|
||||
<span class="text-[1.4rem] mb-[1rem] text-gray-500">
|
||||
<p class="text-[1.6rem] font-semibold text-primary mb-[2rem]">{$t('t_35_1746773362992')}</p>
|
||||
<span class="text-[1.4rem] mb-[1rem] text-color5">
|
||||
{$t(
|
||||
'本工具可帮助用户轻松管理多个网站的SSL证书,提供自动化的证书申请、更新和部署流程,并实时监控证书状态,确保网站安全持续运行。',
|
||||
)}
|
||||
@@ -142,7 +152,7 @@ export default defineComponent({
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -1,44 +1,48 @@
|
||||
import { NCard, NButton, NGrid, NGridItem, NTooltip } from 'naive-ui'
|
||||
import { $t } from '@locales/index'
|
||||
import { useStore } from '../useStore'
|
||||
import { useController, useGeneralSettingsController } from '../useController'
|
||||
import { NCard, NButton, NTooltip } from "naive-ui";
|
||||
import { $t } from "@locales/index";
|
||||
import { useStore } from "../useStore";
|
||||
import { useController, useGeneralSettingsController } from "../useController";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
/**
|
||||
* 常用设置标签页组件
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'GeneralSettings',
|
||||
setup() {
|
||||
const { generalSettings } = useStore()
|
||||
const { handleSaveGeneralSettings, handleDownloadData } = useController()
|
||||
const { GeneralForm } = useGeneralSettingsController()
|
||||
return () => (
|
||||
<div class="flex flex-col gap-[2rem]">
|
||||
<div class="mt-[2rem] flex justify-between">
|
||||
name: "GeneralSettings",
|
||||
setup() {
|
||||
const { generalSettings } = useStore();
|
||||
const { handleSaveGeneralSettings, handleDownloadData } = useController();
|
||||
const { GeneralForm } = useGeneralSettingsController();
|
||||
return () => (
|
||||
<div class={`${styles.generalSettingsCard} flex flex-col`}>
|
||||
<NCard class={styles.baseCard}>
|
||||
<GeneralForm labelPlacement="top" class={styles.formGrid} />
|
||||
</NCard>
|
||||
<div class="mt-[1rem] flex">
|
||||
<NButton
|
||||
type="primary"
|
||||
class="mr-[1rem]"
|
||||
type="primary"
|
||||
class="gradient-primary-btn mr-[2rem]"
|
||||
onClick={() => handleSaveGeneralSettings(generalSettings.value)}
|
||||
>
|
||||
{$t("t_9_1745464078110")}
|
||||
</NButton>
|
||||
<NTooltip
|
||||
v-slots={{
|
||||
trigger: () => <NButton type="primary" onClick={handleDownloadData}>下载数据</NButton>,
|
||||
trigger: () => (
|
||||
<NButton
|
||||
class="gradient-primary-btn"
|
||||
type="primary"
|
||||
onClick={handleDownloadData}
|
||||
>
|
||||
下载数据
|
||||
</NButton>
|
||||
),
|
||||
}}
|
||||
>
|
||||
下载工作流、通知、证书、api授权数据,可直接将数据库文件复制到allinssl的data下使用
|
||||
</NTooltip>
|
||||
</div>
|
||||
|
||||
<NCard title={$t("t_10_1745464073098")} class="mb-4">
|
||||
<NGrid cols="1 m:2" xGap={24} yGap={24}>
|
||||
<NGridItem>
|
||||
<GeneralForm labelPlacement="top" />
|
||||
</NGridItem>
|
||||
</NGrid>
|
||||
</NCard>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
.baseCard {
|
||||
border: 0!important;
|
||||
:global(.n-card-header),
|
||||
:global(.n-card__content) {
|
||||
padding: 0!important;
|
||||
}
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
position: relative;
|
||||
padding-left: 1.4rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sectionTitle::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 50%;
|
||||
background-color: currentColor;
|
||||
}
|
||||
|
||||
|
||||
.gradientTag::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
padding: 1px;
|
||||
background: var(--menu-active-gradient);
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.formGrid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 24px;
|
||||
}
|
||||
|
||||
.formGrid :global(.n-form-item) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.generalSettingsCard :global(.n-form-item) {
|
||||
:global(.n-input__border) {
|
||||
display: none;
|
||||
}
|
||||
:global(.n-input),
|
||||
:global(.n-input-number),
|
||||
:global(.n-textarea) {
|
||||
background-color: var(--setting-input-bg) !important;
|
||||
border: none !important;
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
:global(.n-input__input-el),
|
||||
:global(.n-input-number__input-el),
|
||||
:global(.n-textarea__textarea-el) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
.notifyChannelsCard {
|
||||
border: none!important;
|
||||
:global(.n-card__content) {
|
||||
padding: 0!important;
|
||||
}
|
||||
:global(.n-list-item) {
|
||||
border-radius: 6px;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
background-color: var(--setting-input-bg);
|
||||
}
|
||||
:global(.table-action-btn-danger .n-button__border),
|
||||
:global(.table-action-btn-danger .n-button__state-border),
|
||||
:global(.table-action-btn .n-button__state-border) {
|
||||
display: none!important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.formGrid :global(.n-form-item) {
|
||||
width: calc(50% - 12px);
|
||||
}
|
||||
}
|
||||
|
||||
:global(.defaultDark) {
|
||||
.gradientTag :global(.n-tag__border) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gradientTag {
|
||||
color: transparent;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.gradientTag :global(.n-tag__content) {
|
||||
background: var(--menu-active-gradient);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
:global(.n-alert-body__title) {
|
||||
display: inline-flex;
|
||||
width: auto !important;
|
||||
font-weight: bold!important;
|
||||
margin-top: 6px;
|
||||
background: var(--menu-active-gradient) !important;
|
||||
background-clip: text !important;
|
||||
-webkit-background-clip: text !important;
|
||||
color: transparent !important;
|
||||
-webkit-text-fill-color: transparent !important;
|
||||
}
|
||||
:global(.n-alert) {
|
||||
background-color: #171717;
|
||||
padding-bottom: 1rem;
|
||||
:global(.n-alert__icon) {
|
||||
margin-top: 17px;
|
||||
}
|
||||
}
|
||||
:global(.n-alert__border) {
|
||||
display: none!important;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,23 @@
|
||||
import { NCard, NButton, NList, NListItem, NTag, NSpace, NGrid, NGridItem, NSwitch } from 'naive-ui'
|
||||
import { NCard, NButton, NList, NListItem, NTag, NSpace, NGrid, NGridItem, NSwitch, NIcon } from 'naive-ui'
|
||||
import { WarningFilled, BellFilled } from "@vicons/antd";
|
||||
import { useController } from '@settings/useController'
|
||||
import { useStore } from '@settings/useStore'
|
||||
import SvgIcon from '@components/SvgIcon'
|
||||
import { $t } from '@locales/index'
|
||||
import styles from './index.module.css'
|
||||
|
||||
/**
|
||||
* 告警通知标签页组件
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'NotificationSettings',
|
||||
setup() {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '告警通知',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { notifyChannels, channelTypes } = useStore()
|
||||
const {
|
||||
openAddEmailChannelModal,
|
||||
@@ -38,19 +46,19 @@ export default defineComponent({
|
||||
// 根据类型返回对应的按钮
|
||||
if (type === 'mail') {
|
||||
return (
|
||||
<NButton strong secondary type="primary" onClick={() => openAddEmailChannelModal(getConfiguredCount(type))}>
|
||||
<NButton strong secondary type="primary" class="gradient-primary-btn" onClick={() => openAddEmailChannelModal(getConfiguredCount(type))}>
|
||||
{$t('t_1_1746676859550')}
|
||||
</NButton>
|
||||
)
|
||||
} else if (type === 'feishu') {
|
||||
return (
|
||||
<NButton strong secondary type="primary" onClick={() => openAddFeishuChannelModal(getConfiguredCount(type))}>
|
||||
<NButton strong secondary type="primary" class="gradient-primary-btn" onClick={() => openAddFeishuChannelModal(getConfiguredCount(type))}>
|
||||
{$t('t_1_1746676859550')}
|
||||
</NButton>
|
||||
)
|
||||
} else if (type === 'webhook') {
|
||||
return (
|
||||
<NButton strong secondary type="primary" onClick={() => openAddWebhookChannelModal(getConfiguredCount(type))}>
|
||||
<NButton strong secondary type="primary" class="gradient-primary-btn" onClick={() => openAddWebhookChannelModal(getConfiguredCount(type))}>
|
||||
{$t('t_1_1746676859550')}
|
||||
</NButton>
|
||||
)
|
||||
@@ -60,6 +68,7 @@ export default defineComponent({
|
||||
strong
|
||||
secondary
|
||||
type="primary"
|
||||
class="gradient-primary-btn"
|
||||
onClick={() => openAddDingtalkChannelModal(getConfiguredCount(type))}
|
||||
>
|
||||
{$t('t_1_1746676859550')}
|
||||
@@ -67,14 +76,14 @@ export default defineComponent({
|
||||
)
|
||||
} else if (type === 'workwx') {
|
||||
return (
|
||||
<NButton strong secondary type="primary" onClick={() => openAddWecomChannelModal(getConfiguredCount(type))}>
|
||||
<NButton strong secondary type="primary" class="gradient-primary-btn" onClick={() => openAddWecomChannelModal(getConfiguredCount(type))}>
|
||||
{$t('t_1_1746676859550')}
|
||||
</NButton>
|
||||
)
|
||||
}
|
||||
// 其他渠道暂未支持
|
||||
return (
|
||||
<NButton strong secondary disabled>
|
||||
<NButton strong secondary disabled class="gradient-default-btn">
|
||||
{$t('t_2_1746676856700')}
|
||||
</NButton>
|
||||
)
|
||||
@@ -115,23 +124,29 @@ export default defineComponent({
|
||||
]
|
||||
return () => (
|
||||
<div class="notification-settings">
|
||||
<NCard title={$t('t_13_1746676856842')} class="mb-4">
|
||||
<div class="mb-4 px-[2rem] py-[2.4rem] bg-[var(--content-bg-base)] rounded-[6px]">
|
||||
<div class="flex items-center mb-6">
|
||||
<NIcon size="24">
|
||||
<WarningFilled />
|
||||
</NIcon>
|
||||
<h2 class="ml-2 text-[1.8rem] font-semibold">{props.title}</h2>
|
||||
</div>
|
||||
<NGrid cols="2 s:1 m:2" xGap={16} yGap={16}>
|
||||
{channelConfigs.map((item) => (
|
||||
<NGridItem key={item.type}>
|
||||
<div class="flex justify-between items-center p-4 border border-[var(--n-border-color)] rounded-md hover:shadow-sm transition-shadow">
|
||||
<div class="flex justify-between items-center p-8 bg-[var(--setting-input-bg)] rounded-md hover:shadow-sm transition-shadow">
|
||||
<div class="flex items-center">
|
||||
<SvgIcon icon={`notify-${item.type}`} size="3rem" />
|
||||
<SvgIcon icon={`notify-${item.type}`} size="4rem" />
|
||||
<div class="ml-4">
|
||||
<div class="flex items-center mb-1">
|
||||
<span class="mr-2 font-medium">{item.name}</span>
|
||||
<span class="mr-2 font-bold">{item.name}</span>
|
||||
{isChannelConfigured(item.type) && (
|
||||
<NTag size="small" type="success">
|
||||
<NTag size="small" round class={styles.gradientTag} type="success">
|
||||
{$t('t_8_1745735765753')} {getConfiguredCount(item.type)}
|
||||
</NTag>
|
||||
)}
|
||||
</div>
|
||||
<div class="text-gray-500 text-[1.2rem]">{item.description}</div>
|
||||
<div class="text-color5 text-[1.2rem]">{item.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>{getChannelActionButton(item.type)}</div>
|
||||
@@ -139,54 +154,62 @@ export default defineComponent({
|
||||
</NGridItem>
|
||||
))}
|
||||
</NGrid>
|
||||
</NCard>
|
||||
</div>
|
||||
|
||||
{/* 已配置的通知渠道列表 */}
|
||||
{notifyChannels.value.length > 0 && (
|
||||
<NCard title={$t('t_14_1746676859019')} class="mb-4">
|
||||
<NList bordered>
|
||||
{notifyChannels.value.map((item) => (
|
||||
<NListItem key={item.id}>
|
||||
<div class=" items-center justify-between p-2 grid grid-cols-12">
|
||||
<div class="flex items-center col-span-6">
|
||||
<SvgIcon icon={`notify-${item.type}`} size="3rem" />
|
||||
<div class="font-medium mx-[1rem]">{item.name}</div>
|
||||
<div class="flex items-center ">
|
||||
<NTag type="info" size="small">
|
||||
{(channelTypes.value as Record<string, string>)[item.type] || item.id}
|
||||
</NTag>
|
||||
<div class="noti-settings-container px-[2rem] py-[2.4rem] bg-[var(--content-bg-base)] rounded-[6px]">
|
||||
<div class="flex items-center mb-6">
|
||||
<NIcon size="24">
|
||||
<BellFilled />
|
||||
</NIcon>
|
||||
<h2 class="ml-2 text-[1.8rem] font-semibold">已配置的通知渠道</h2>
|
||||
</div>
|
||||
<NCard class={styles.notifyChannelsCard}>
|
||||
<NList show-divider={false} class="flex flex-col gap-6">
|
||||
{notifyChannels.value.map((item) => (
|
||||
<NListItem key={item.id}>
|
||||
<div class=" items-center justify-between p-2 grid grid-cols-12">
|
||||
<div class="flex items-center col-span-6">
|
||||
<SvgIcon icon={`notify-${item.type}`} size="3rem" />
|
||||
<div class="font-medium mx-[1rem]">{item.name}</div>
|
||||
<div class="flex items-center ">
|
||||
<NTag type="info" class={styles.gradientTag} round size="small">
|
||||
{(channelTypes.value as Record<string, string>)[item.type] || item.id}
|
||||
</NTag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 col-span-3 justify-end">
|
||||
<NSwitch
|
||||
v-model:value={item.config.enabled}
|
||||
onUpdateValue={() => handleEnableChange(item)}
|
||||
checkedValue={'1'}
|
||||
uncheckedValue={'0'}
|
||||
v-slots={{
|
||||
checked: () => <span>{$t('t_0_1745457486299')}</span>,
|
||||
unchecked: () => <span>{$t('t_15_1746676856567')}</span>,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-8 col-span-3 justify-end">
|
||||
<NSpace>
|
||||
<NButton class="table-action-btn" size="small" onClick={() => editChannelConfig(item)}>
|
||||
{$t('t_11_1745215915429')}
|
||||
</NButton>
|
||||
<NButton class="table-action-btn" size="small" onClick={() => testChannelConfig(item)}>
|
||||
{$t('t_16_1746676855270')}
|
||||
</NButton>
|
||||
<NButton class="table-action-btn-danger" size="small" type="error" onClick={() => confirmDeleteChannel(item)}>
|
||||
{$t('t_12_1745215914312')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 col-span-3 justify-end">
|
||||
<NSwitch
|
||||
v-model:value={item.config.enabled}
|
||||
onUpdateValue={() => handleEnableChange(item)}
|
||||
checkedValue={'1'}
|
||||
uncheckedValue={'0'}
|
||||
v-slots={{
|
||||
checked: () => <span>{$t('t_0_1745457486299')}</span>,
|
||||
unchecked: () => <span>{$t('t_15_1746676856567')}</span>,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-8 col-span-3 justify-end">
|
||||
<NSpace>
|
||||
<NButton size="small" onClick={() => editChannelConfig(item)}>
|
||||
{$t('t_11_1745215915429')}
|
||||
</NButton>
|
||||
<NButton size="small" onClick={() => testChannelConfig(item)}>
|
||||
{$t('t_16_1746676855270')}
|
||||
</NButton>
|
||||
<NButton size="small" type="error" onClick={() => confirmDeleteChannel(item)}>
|
||||
{$t('t_12_1745215914312')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</div>
|
||||
</div>
|
||||
</NListItem>
|
||||
))}
|
||||
</NList>
|
||||
</NCard>
|
||||
</NListItem>
|
||||
))}
|
||||
</NList>
|
||||
</NCard>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,86 +1,85 @@
|
||||
import { NTabs, NTabPane, NCard, NIcon } from 'naive-ui'
|
||||
import { SettingOutlined, BellOutlined, InfoCircleOutlined } from '@vicons/antd'
|
||||
import { NCard, NIcon } from "naive-ui";
|
||||
import {
|
||||
SettingFilled,
|
||||
BellOutlined,
|
||||
InfoCircleFilled,
|
||||
} from "@vicons/antd";
|
||||
|
||||
import { useStore } from './useStore'
|
||||
import { useController } from './useController'
|
||||
import BaseComponent from '@components/BaseLayout'
|
||||
import GeneralSettings from './components/GeneralSettings'
|
||||
import NotificationSettings from './components/NotificationSettings'
|
||||
import AboutSettings from './components/AboutSettings'
|
||||
import { useStore } from "./useStore";
|
||||
import { useController } from "./useController";
|
||||
import BaseComponent from "@components/BaseLayout";
|
||||
import GeneralSettings from "./components/GeneralSettings";
|
||||
import NotificationSettings from "./components/NotificationSettings";
|
||||
import AboutSettings from "./components/AboutSettings";
|
||||
|
||||
/**
|
||||
* 设置页面组件
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'Settings',
|
||||
setup() {
|
||||
const { activeTab, tabOptions } = useStore()
|
||||
const { fetchAllSettings, isCutTab } = useController()
|
||||
name: "Settings",
|
||||
setup() {
|
||||
const { tabOptions } = useStore();
|
||||
const { fetchAllSettings } = useController();
|
||||
|
||||
// 渲染图标组件
|
||||
const renderIcon = (iconName: string) => {
|
||||
const icons: Record<string, any> = {
|
||||
SettingOutlined: <SettingOutlined />,
|
||||
BellOutlined: <BellOutlined />,
|
||||
InfoCircleOutlined: <InfoCircleOutlined />,
|
||||
}
|
||||
return <NIcon size="20">{icons[iconName]}</NIcon>
|
||||
}
|
||||
// 渲染图标组件
|
||||
const renderIcon = (iconName: string) => {
|
||||
const icons: Record<string, any> = {
|
||||
SettingFilled: <SettingFilled />,
|
||||
BellOutlined: <BellOutlined />,
|
||||
InfoCircleFilled: <InfoCircleFilled />,
|
||||
};
|
||||
return <NIcon size="20">{icons[iconName]}</NIcon>;
|
||||
};
|
||||
|
||||
// 自定义Tab样式已移至全局reset.css
|
||||
onMounted(() => {
|
||||
fetchAllSettings();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
isCutTab()
|
||||
fetchAllSettings()
|
||||
})
|
||||
return () => (
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="mx-auto max-w-[1600px] w-full p-6">
|
||||
<BaseComponent
|
||||
v-slots={{
|
||||
content: () => (
|
||||
<div class="w-full space-y-6">
|
||||
{/* 常用设置 */}
|
||||
<NCard>
|
||||
<div>
|
||||
<div class="flex items-center mb-6">
|
||||
{renderIcon("SettingFilled")}
|
||||
<h2 class="ml-2 text-[1.8rem] font-semibold">
|
||||
{tabOptions.value[0]?.title || "常用设置"}
|
||||
</h2>
|
||||
</div>
|
||||
<GeneralSettings />
|
||||
</div>
|
||||
</NCard>
|
||||
|
||||
return () => (
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="mx-auto max-w-[1600px] w-full p-6">
|
||||
<BaseComponent
|
||||
v-slots={{
|
||||
content: () => (
|
||||
<div class="w-full">
|
||||
<NCard>
|
||||
<NTabs
|
||||
class="rounded-2xl p-6"
|
||||
type="segment"
|
||||
v-model:value={activeTab.value}
|
||||
size="large"
|
||||
justifyContent="space-evenly"
|
||||
>
|
||||
{tabOptions.value.map((tab) => (
|
||||
<NTabPane key={tab.key} name={tab.key}>
|
||||
{{
|
||||
tab: () => (
|
||||
<div class="flex items-center my-[10px] px-2 py-1 rounded-lg transition-all duration-300 ease-in-out">
|
||||
{renderIcon(tab.icon)}
|
||||
<span class="ml-2">{tab.title}</span>
|
||||
</div>
|
||||
),
|
||||
default: () => (
|
||||
<div class="w-full">
|
||||
{/* 常用设置 */}
|
||||
{activeTab.value === 'general' && <GeneralSettings />}
|
||||
{/* 告警通知 */}
|
||||
<div>
|
||||
<NotificationSettings
|
||||
title={tabOptions.value[1]?.title || "告警通知"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 告警通知 */}
|
||||
{activeTab.value === 'notification' && <NotificationSettings />}
|
||||
|
||||
{/* 关于我们 */}
|
||||
{activeTab.value === 'about' && <AboutSettings />}
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</NTabPane>
|
||||
))}
|
||||
</NTabs>
|
||||
</NCard>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
{/* 关于我们 */}
|
||||
<NCard>
|
||||
<div class="p-6">
|
||||
<div class="flex items-center mb-6">
|
||||
{renderIcon("InfoCircleFilled")}
|
||||
<h2 class="ml-2 text-[1.8rem] font-semibold">
|
||||
{tabOptions.value[2]?.title || "关于我们"}
|
||||
</h2>
|
||||
</div>
|
||||
<AboutSettings />
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AddNode: typeof import('./../src/components/flowChart/components/other/addNode/index.tsx')['default']
|
||||
Alias: typeof import('./../src/components/flowChart/lib/alias.tsx')['default']
|
||||
AllinSslThemeProvider: typeof import('./../src/components/AllinSslThemeProvider/index.tsx')['default']
|
||||
BaseLayout: typeof import('./../src/components/BaseLayout/index.tsx')['default']
|
||||
BaseNode: typeof import('./../src/components/flowChart/components/base/baseNode/index.tsx')['default']
|
||||
BranchNode: typeof import('./../src/components/flowChart/components/base/branchNode/index.tsx')['default']
|
||||
@@ -22,14 +23,18 @@ declare module 'vue' {
|
||||
EndNode: typeof import('./../src/components/flowChart/components/base/endNode.tsx')['default']
|
||||
ErrorNode: typeof import('./../src/components/flowChart/components/base/errorNode/index.tsx')['default']
|
||||
FlowChart: typeof import('./../src/components/flowChart/index.tsx')['default']
|
||||
InfoItem: typeof import('./../src/components/InfoItem/index.tsx')['default']
|
||||
LogDisplay: typeof import('./../src/components/LogDisplay/index.tsx')['default']
|
||||
NodeWrap: typeof import('./../src/components/flowChart/components/render/nodeWrap.tsx')['default']
|
||||
NotifyProviderMultiSelect: typeof import('./../src/components/notifyProviderMultiSelect/index.tsx')['default']
|
||||
NotifyProviderSelect: typeof import('./../src/components/notifyProviderSelect/index.tsx')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
StatusCard: typeof import('./../src/components/StatusCard/index.tsx')['default']
|
||||
SvgIcon: typeof import('./../src/components/svgIcon/index.tsx')['default']
|
||||
TableEmptyState: typeof import('./../src/components/TableEmptyState/index.tsx')['default']
|
||||
ThemeProvider: typeof import('./../src/components/ThemeProvider/index.tsx')['default']
|
||||
ThemeToggle: typeof import('./../src/components/ThemeToggle/index.tsx')['default']
|
||||
TypeIcon: typeof import('./../src/components/typeIcon/index.tsx')['default']
|
||||
UpdateLogModal: typeof import('./../src/components/UpdateLogModal/index.tsx')['default']
|
||||
UseController: typeof import('./../src/components/CAProviderSelect/useController.tsx')['default']
|
||||
|
||||
@@ -21,223 +21,213 @@ const isDev = process.env.NODE_ENV === 'development' // 开发环境
|
||||
|
||||
/// <reference types="vitest" />
|
||||
export default defineConfig({
|
||||
mode: isDev ? "development" : "production",
|
||||
base: "./",
|
||||
// 插件配置
|
||||
plugins: [
|
||||
// vue处理
|
||||
Vue({
|
||||
script: {
|
||||
defineModel: true,
|
||||
},
|
||||
}),
|
||||
mode: isDev ? 'development' : 'production',
|
||||
base: './',
|
||||
// 插件配置
|
||||
plugins: [
|
||||
// vue处理
|
||||
Vue({
|
||||
script: {
|
||||
defineModel: true,
|
||||
},
|
||||
}),
|
||||
|
||||
// jsx处理
|
||||
VueJsx(),
|
||||
// 压缩gzip
|
||||
// compression(),
|
||||
// 方法自动引入
|
||||
AutoImport({
|
||||
imports: ["vue", "@vueuse/core", "pinia", "vue-router"],
|
||||
dts: "./types/auto-imports.d.ts", // 生成的d.ts文件
|
||||
eslintrc: {
|
||||
enabled: true, // 默认false, true启用。生成一次就可以,避免每次工程启动都生成
|
||||
filepath: "./types/.eslintrc-auto-import.json", // 生成json文件
|
||||
globalsPropValue: true,
|
||||
},
|
||||
}),
|
||||
// jsx处理
|
||||
VueJsx(),
|
||||
// 压缩gzip
|
||||
// compression(),
|
||||
// 方法自动引入
|
||||
AutoImport({
|
||||
imports: ['vue', '@vueuse/core', 'pinia', 'vue-router'],
|
||||
dts: './types/auto-imports.d.ts', // 生成的d.ts文件
|
||||
eslintrc: {
|
||||
enabled: true, // 默认false, true启用。生成一次就可以,避免每次工程启动都生成
|
||||
filepath: './types/.eslintrc-auto-import.json', // 生成json文件
|
||||
globalsPropValue: true,
|
||||
},
|
||||
}),
|
||||
|
||||
// 组件自动引入
|
||||
Components({
|
||||
dts: "./types/components.d.ts", // 生成的d.ts文件
|
||||
dirs: ["src/components"], // 指定扫描的文件夹
|
||||
extensions: ["vue", "tsx"], // 指定扫描的文件类型
|
||||
directoryAsNamespace: false, // 是否将目录作为命名空间
|
||||
resolvers: [NaiveUiResolver()],
|
||||
}),
|
||||
// 组件自动引入
|
||||
Components({
|
||||
dts: './types/components.d.ts', // 生成的d.ts文件
|
||||
dirs: ['src/components'], // 指定扫描的文件夹
|
||||
extensions: ['vue', 'tsx'], // 指定扫描的文件类型
|
||||
directoryAsNamespace: false, // 是否将目录作为命名空间
|
||||
resolvers: [NaiveUiResolver()],
|
||||
}),
|
||||
|
||||
// https协议兼容
|
||||
// basicSsl(),
|
||||
// https协议兼容
|
||||
// basicSsl(),
|
||||
|
||||
// 浏览器兼容
|
||||
isDev &&
|
||||
legacy({
|
||||
targets: [">0.1%", "Firefox > 55", "Chrome > 60", "safari > 11"],
|
||||
}),
|
||||
// 浏览器兼容
|
||||
isDev &&
|
||||
legacy({
|
||||
targets: ['>0.1%', 'Firefox > 55', 'Chrome > 60', 'safari > 11'],
|
||||
}),
|
||||
|
||||
// vue3调试工具
|
||||
VueDevTools(),
|
||||
// vue3调试工具
|
||||
VueDevTools(),
|
||||
|
||||
// 创建 svg 图标
|
||||
createSvgIconsPlugin({
|
||||
iconDirs: [path.resolve(process.cwd(), "src/assets/icons/svg/")],
|
||||
symbolId: "icon-[dir]-[name]",
|
||||
}),
|
||||
// vite mcp 引入,解决数据构建文件
|
||||
VueMcp(),
|
||||
// i18n生成器
|
||||
pluginI18n(),
|
||||
// ftp同步
|
||||
ftpSync([
|
||||
// {
|
||||
// host: '192.168.168.121',
|
||||
// port: 22,
|
||||
// username: 'root',
|
||||
// password: 'www.bt.cn',
|
||||
// remotePath: '/www/allinssl/frontend',
|
||||
// clearRemote: true,
|
||||
// },
|
||||
// {
|
||||
// host: '192.168.69.167',
|
||||
// port: 22,
|
||||
// username: 'root',
|
||||
// password: 'www.bt.cn',
|
||||
// remotePath: '/www/allinssl/frontend',
|
||||
// clearRemote: true,
|
||||
// },
|
||||
]),
|
||||
// 项目同步git
|
||||
pluginProjectSyncGit({
|
||||
gitProjects: [
|
||||
// {
|
||||
// repo: 'ssh://git@git.bt.cn:30001/wzz/allinssl.git',
|
||||
// branch: '1.0.2',
|
||||
// targetDir: 'allinssl-gitlab',
|
||||
// discardChanges: true,
|
||||
// },
|
||||
{
|
||||
repo: "https://github.com/allinssl/allinssl.git",
|
||||
branch: "1.1.0",
|
||||
targetDir: "allinssl-github",
|
||||
discardChanges: true,
|
||||
},
|
||||
],
|
||||
localSync: [
|
||||
{
|
||||
source: "apps/allin-ssl/dist",
|
||||
target: [
|
||||
".sync-git/allinssl-gitlab/static/build",
|
||||
".sync-git/allinssl-github/static/build",
|
||||
],
|
||||
mode: "copy",
|
||||
clearTarget: true,
|
||||
exclude: ["node_modules", ".git"],
|
||||
},
|
||||
{
|
||||
source: "/",
|
||||
target: [
|
||||
".sync-git/allinssl-gitlab/frontend",
|
||||
".sync-git/allinssl-github/frontend",
|
||||
],
|
||||
mode: "copy",
|
||||
clearTarget: true,
|
||||
excludeDirs: [
|
||||
"node_modules",
|
||||
"dist",
|
||||
".sync-git",
|
||||
".sync-log",
|
||||
".cursor",
|
||||
".devcontainer",
|
||||
".github",
|
||||
".git",
|
||||
".test",
|
||||
".vascode",
|
||||
".turbo",
|
||||
"apps/cloud-control",
|
||||
"apps/monorepo-docs",
|
||||
"apps/naive-template",
|
||||
"apps/vueFlow",
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
// 别名配置
|
||||
alias: {
|
||||
"@layout": path.resolve(__dirname, "src/views/layout"),
|
||||
"@login": path.resolve(__dirname, "src/views/login"),
|
||||
"@404": path.resolve(__dirname, "src/views/404"),
|
||||
"@certManage": path.resolve(__dirname, "src/views/certManage"),
|
||||
"@certApply": path.resolve(__dirname, "src/views/certApply"),
|
||||
"@autoDeploy": path.resolve(__dirname, "src/views/autoDeploy"),
|
||||
"@workflowView": path.resolve(
|
||||
__dirname,
|
||||
"src/views/autoDeploy/children/workflowView"
|
||||
),
|
||||
"@autoApiManage": path.resolve(__dirname, "src/views/autoApiManage"),
|
||||
"@home": path.resolve(__dirname, "src/views/home"),
|
||||
"@monitor": path.resolve(__dirname, "src/views/monitor"),
|
||||
"@settings": path.resolve(__dirname, "src/views/settings"),
|
||||
"@test": path.resolve(__dirname, "src/views/test"),
|
||||
"@api": path.resolve(__dirname, "src/api"),
|
||||
"@assets": path.resolve(__dirname, "src/assets"),
|
||||
"@components": path.resolve(__dirname, "src/components"),
|
||||
"@public": path.resolve(__dirname, "src/public"),
|
||||
"@router": path.resolve(__dirname, "src/router"),
|
||||
"@locales": path.resolve(__dirname, "src/locales"),
|
||||
"@config": path.resolve(__dirname, "src/config"),
|
||||
"@styles": path.resolve(__dirname, "src/styles"),
|
||||
"@types": path.resolve(__dirname, "src/types"),
|
||||
"@lib": path.resolve(__dirname, "src/lib"),
|
||||
"@": path.resolve(__dirname, "src"),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
minify: "terser", // 混淆器,terser构建后文件体积更小
|
||||
// assetsDir: `${packPath}/`, // 静态资源目录
|
||||
sourcemap: false,
|
||||
cssCodeSplit: false, // 不分割css代码
|
||||
reportCompressedSize: false, // 不统计gzip压缩后的文件大小
|
||||
chunkSizeWarningLimit: 800, // 警告阈值
|
||||
assetsInlineLimit: 2048, // 小于2kb的资源内联
|
||||
modulePreload: false, // 禁用预加载
|
||||
terserOptions: {
|
||||
// 打包后移除console和注释
|
||||
compress: {
|
||||
drop_console: !isDev, // 生产环境移除console
|
||||
drop_debugger: !isDev, // 生产环境移除debugger
|
||||
},
|
||||
},
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: resolve(__dirname, "index.html"), // 主页面
|
||||
},
|
||||
strictDeprecations: true, // 严格弃用
|
||||
output: {
|
||||
entryFileNames: `${packPath}js/[name]-[hash].js`,
|
||||
chunkFileNames: `${packPath}js/[name]-[hash].js`,
|
||||
assetFileNames: (chunkInfo) => {
|
||||
const { names } = chunkInfo;
|
||||
let ext = "[ext]";
|
||||
if (names && names.length > 0) {
|
||||
const name = names[0];
|
||||
const str = name.substring(name.lastIndexOf(".") + 1);
|
||||
if (str === "ttf" || str === "woff" || str === "woff2")
|
||||
ext = "font";
|
||||
}
|
||||
return `${packPath}${ext}/[name]-[hash].[ext]`;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
server: {
|
||||
// https: { rejectUnauthorized: false },
|
||||
host: "0.0.0.0",
|
||||
port: 5173,
|
||||
proxy: {
|
||||
"/api": {
|
||||
// target: `http://${'192.168.168.25'}:${37628}`,
|
||||
// target: `http://${'192.168.168.121'}:${33488}`,
|
||||
target: `http://${"192.168.168.64"}:${20773}`,
|
||||
changeOrigin: true, // 是否改变源
|
||||
rewrite: (path: string) => path.replace(/^\/api/, ""), // 重写路径
|
||||
secure: false, // 如果是https接口,需要配置这个参数
|
||||
ws: false, // 是否启用websocket
|
||||
},
|
||||
},
|
||||
},
|
||||
test: {
|
||||
include: ["src/**/*.spec.ts"],
|
||||
},
|
||||
});
|
||||
// 创建 svg 图标
|
||||
createSvgIconsPlugin({
|
||||
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg/')],
|
||||
symbolId: 'icon-[dir]-[name]',
|
||||
}),
|
||||
// vite mcp 引入,解决数据构建文件
|
||||
VueMcp(),
|
||||
// i18n生成器
|
||||
pluginI18n(),
|
||||
// ftp同步
|
||||
ftpSync([
|
||||
// {
|
||||
// host: '192.168.168.121',
|
||||
// port: 22,
|
||||
// username: 'root',
|
||||
// password: 'www.bt.cn',
|
||||
// remotePath: '/www/allinssl/frontend',
|
||||
// clearRemote: true,
|
||||
// },
|
||||
// {
|
||||
// host: '192.168.69.167',
|
||||
// port: 22,
|
||||
// username: 'root',
|
||||
// password: 'www.bt.cn',
|
||||
// remotePath: '/www/allinssl/frontend',
|
||||
// clearRemote: true,
|
||||
// },
|
||||
]),
|
||||
// 项目同步git
|
||||
pluginProjectSyncGit({
|
||||
gitProjects: [
|
||||
// {
|
||||
// repo: 'ssh://git@git.bt.cn:30001/wzz/allinssl.git',
|
||||
// branch: '1.0.2',
|
||||
// targetDir: 'allinssl-gitlab',
|
||||
// discardChanges: true,
|
||||
// },
|
||||
{
|
||||
repo: 'https://github.com/allinssl/allinssl.git',
|
||||
branch: '1.1.1',
|
||||
targetDir: 'allinssl-github',
|
||||
discardChanges: true,
|
||||
},
|
||||
],
|
||||
localSync: [
|
||||
{
|
||||
source: 'apps/allin-ssl/dist',
|
||||
target: ['.sync-git/allinssl-gitlab/static/build', '.sync-git/allinssl-github/static/build'],
|
||||
mode: 'copy',
|
||||
clearTarget: true,
|
||||
exclude: ['node_modules', '.git'],
|
||||
},
|
||||
{
|
||||
source: '/',
|
||||
target: ['.sync-git/allinssl-gitlab/frontend', '.sync-git/allinssl-github/frontend'],
|
||||
mode: 'copy',
|
||||
clearTarget: true,
|
||||
excludeDirs: [
|
||||
'node_modules',
|
||||
'dist',
|
||||
'.sync-git',
|
||||
'.sync-log',
|
||||
'.cursor',
|
||||
'.devcontainer',
|
||||
'.github',
|
||||
'.git',
|
||||
'.test',
|
||||
'.vascode',
|
||||
'.turbo',
|
||||
'apps/cloud-control',
|
||||
'apps/monorepo-docs',
|
||||
'apps/naive-template',
|
||||
'apps/vueFlow',
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
// 别名配置
|
||||
alias: {
|
||||
'@layout': path.resolve(__dirname, 'src/views/layout'),
|
||||
'@login': path.resolve(__dirname, 'src/views/login'),
|
||||
'@404': path.resolve(__dirname, 'src/views/404'),
|
||||
'@certManage': path.resolve(__dirname, 'src/views/certManage'),
|
||||
'@certApply': path.resolve(__dirname, 'src/views/certApply'),
|
||||
'@autoDeploy': path.resolve(__dirname, 'src/views/autoDeploy'),
|
||||
'@workflowView': path.resolve(__dirname, 'src/views/autoDeploy/children/workflowView'),
|
||||
'@autoApiManage': path.resolve(__dirname, 'src/views/autoApiManage'),
|
||||
'@home': path.resolve(__dirname, 'src/views/home'),
|
||||
'@monitor': path.resolve(__dirname, 'src/views/monitor'),
|
||||
'@settings': path.resolve(__dirname, 'src/views/settings'),
|
||||
'@test': path.resolve(__dirname, 'src/views/test'),
|
||||
'@api': path.resolve(__dirname, 'src/api'),
|
||||
'@assets': path.resolve(__dirname, 'src/assets'),
|
||||
'@components': path.resolve(__dirname, 'src/components'),
|
||||
'@public': path.resolve(__dirname, 'src/public'),
|
||||
'@router': path.resolve(__dirname, 'src/router'),
|
||||
'@locales': path.resolve(__dirname, 'src/locales'),
|
||||
'@config': path.resolve(__dirname, 'src/config'),
|
||||
'@styles': path.resolve(__dirname, 'src/styles'),
|
||||
'@types': path.resolve(__dirname, 'src/types'),
|
||||
'@lib': path.resolve(__dirname, 'src/lib'),
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
minify: 'terser', // 混淆器,terser构建后文件体积更小
|
||||
// assetsDir: `${packPath}/`, // 静态资源目录
|
||||
sourcemap: false,
|
||||
cssCodeSplit: false, // 不分割css代码
|
||||
reportCompressedSize: false, // 不统计gzip压缩后的文件大小
|
||||
chunkSizeWarningLimit: 800, // 警告阈值
|
||||
assetsInlineLimit: 2048, // 小于2kb的资源内联
|
||||
modulePreload: false, // 禁用预加载
|
||||
terserOptions: {
|
||||
// 打包后移除console和注释
|
||||
compress: {
|
||||
drop_console: !isDev, // 生产环境移除console
|
||||
drop_debugger: !isDev, // 生产环境移除debugger
|
||||
},
|
||||
},
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: resolve(__dirname, 'index.html'), // 主页面
|
||||
},
|
||||
strictDeprecations: true, // 严格弃用
|
||||
output: {
|
||||
entryFileNames: `${packPath}js/[name]-[hash].js`,
|
||||
chunkFileNames: `${packPath}js/[name]-[hash].js`,
|
||||
assetFileNames: (chunkInfo) => {
|
||||
const { names } = chunkInfo
|
||||
let ext = '[ext]'
|
||||
if (names && names.length > 0) {
|
||||
const name = names[0]
|
||||
const str = name.substring(name.lastIndexOf('.') + 1)
|
||||
if (str === 'ttf' || str === 'woff' || str === 'woff2') ext = 'font'
|
||||
}
|
||||
return `${packPath}${ext}/[name]-[hash].[ext]`
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
server: {
|
||||
// https: { rejectUnauthorized: false },
|
||||
host: '0.0.0.0',
|
||||
port: 5173,
|
||||
proxy: {
|
||||
'/api': {
|
||||
// target: `http://${'192.168.168.25'}:${37628}`,
|
||||
// target: `http://${'192.168.168.121'}:${33488}`,
|
||||
target: `http://${'192.168.168.64'}:${20773}`,
|
||||
changeOrigin: true, // 是否改变源
|
||||
rewrite: (path: string) => path.replace(/^\/api/, ''), // 重写路径
|
||||
secure: false, // 如果是https接口,需要配置这个参数
|
||||
ws: false, // 是否启用websocket
|
||||
},
|
||||
},
|
||||
},
|
||||
test: {
|
||||
include: ['src/**/*.spec.ts'],
|
||||
},
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description 提供域名列表查询、详情获取、DNS服务器更新、安全设置等功能
|
||||
*/
|
||||
|
||||
import { useApi } from "@api/index";
|
||||
import { useApi } from '@api/index'
|
||||
import type {
|
||||
DomainListRequest,
|
||||
DomainListResponse,
|
||||
@@ -21,12 +21,17 @@ import type {
|
||||
DomainRealNameUpdateRequest,
|
||||
PrivacyRequest,
|
||||
PrivacyPriceRequest,
|
||||
DeleteDomainRequest,
|
||||
DeleteDomainApiResponse,
|
||||
UpdatePrivacyInfoRequest,
|
||||
UpdatePrivacyInfoApiResponse,
|
||||
DeletePrivacyInfoRequest,
|
||||
DeletePrivacyInfoApiResponse,
|
||||
GetWhoisInfoRequest,
|
||||
GetWhoisInfoApiResponse,
|
||||
} from '@/types/domain'
|
||||
import type {
|
||||
DomainTransferListRequest,
|
||||
DomainTransferListResponse,
|
||||
} from '@/types/transfer'
|
||||
import type {ApiResponse} from '@/types/api'
|
||||
import type { DomainTransferListRequest, DomainTransferListResponse } from '@/types/transfer'
|
||||
import type { ApiResponse } from '@/types/api'
|
||||
|
||||
/**
|
||||
* @description 获取域名列表
|
||||
@@ -35,10 +40,7 @@ import type {ApiResponse} from '@/types/api'
|
||||
* @returns {useAxiosReturn<DomainListResponse, DomainListRequest>} 返回域名列表数据
|
||||
*/
|
||||
export const fetchDomainList = (params: DomainListRequest) =>
|
||||
useApi<DomainListResponse, DomainListRequest>(
|
||||
"/v1/domain/manage/list",
|
||||
params,
|
||||
);
|
||||
useApi<DomainListResponse, DomainListRequest>('/v1/domain/manage/list', params)
|
||||
|
||||
/**
|
||||
* @description 获取指定域名详情
|
||||
@@ -47,10 +49,7 @@ export const fetchDomainList = (params: DomainListRequest) =>
|
||||
* @returns {useAxiosReturn<DomainDetailResponse, DomainDetailRequest>} 返回域名详细信息
|
||||
*/
|
||||
export const fetchDomainDetail = (params: DomainDetailRequest) =>
|
||||
useApi<DomainDetailResponse, DomainDetailRequest>(
|
||||
"/v1/domain/manage/detail",
|
||||
params,
|
||||
);
|
||||
useApi<DomainDetailResponse, DomainDetailRequest>('/v1/domain/manage/detail', params)
|
||||
/**
|
||||
* @description 修改域名实名模板
|
||||
* @param {DomainRealNameUpdateRequest} params 更新参数,包含域名ID和新的实名模板id
|
||||
@@ -66,10 +65,7 @@ export const updateDomainRealName = (params: DomainRealNameUpdateRequest) =>
|
||||
* @returns {useAxiosReturn<UpdateDnsServersResponse, UpdateDnsServersRequest>} 返回更新结果
|
||||
*/
|
||||
export const updateDomainDnsServers = (params: UpdateDnsServersRequest) =>
|
||||
useApi<UpdateDnsServersResponse, UpdateDnsServersRequest>(
|
||||
"/v1/domain/manage/update_dns_servers",
|
||||
params,
|
||||
);
|
||||
useApi<UpdateDnsServersResponse, UpdateDnsServersRequest>('/v1/domain/manage/update_dns_servers', params)
|
||||
|
||||
/**
|
||||
* @description 设置域名安全状态(禁止更新/禁止转移)
|
||||
@@ -78,10 +74,7 @@ export const updateDomainDnsServers = (params: UpdateDnsServersRequest) =>
|
||||
* @returns {useAxiosReturn<SetDomainSecurityResponse, SetDomainSecurityRequest>} 返回设置结果
|
||||
*/
|
||||
export const setDomainSecurity = (params: SetDomainSecurityRequest) =>
|
||||
useApi<SetDomainSecurityResponse, SetDomainSecurityRequest>(
|
||||
"/v1/domain/manage/set_domain_security",
|
||||
params,
|
||||
);
|
||||
useApi<SetDomainSecurityResponse, SetDomainSecurityRequest>('/v1/domain/manage/set_domain_security', params)
|
||||
|
||||
/**
|
||||
* @description 手动刷新域名注册状态
|
||||
@@ -90,31 +83,20 @@ export const setDomainSecurity = (params: SetDomainSecurityRequest) =>
|
||||
* @returns {useAxiosReturn<RefreshDomainStatusResponse, RefreshDomainStatusRequest>} 返回刷新结果
|
||||
*/
|
||||
export const refreshDomainStatus = (params: RefreshDomainStatusRequest) =>
|
||||
useApi<RefreshDomainStatusResponse, RefreshDomainStatusRequest>(
|
||||
"/v1/domain/manage/refresh_domain_status",
|
||||
params,
|
||||
);
|
||||
useApi<RefreshDomainStatusResponse, RefreshDomainStatusRequest>('/v1/domain/manage/refresh_domain_status', params)
|
||||
|
||||
// 新增:域名价格查询
|
||||
export const queryDomainPrice = (params:DomainPriceQueryRequest) =>
|
||||
useApi<DomainPriceQueryResponse,DomainPriceQueryRequest>(
|
||||
'/v1/domain/query/price',
|
||||
params,
|
||||
)
|
||||
export const queryDomainPrice = (params: DomainPriceQueryRequest) =>
|
||||
useApi<DomainPriceQueryResponse, DomainPriceQueryRequest>('/v1/domain/query/price', params)
|
||||
|
||||
// 新增:获取域名转入列表
|
||||
export const fetchDomainTransferList = (params: DomainTransferListRequest = {}) =>
|
||||
useApi<DomainTransferListResponse, DomainTransferListRequest>(
|
||||
'/v1/domain/transfer/get_transfer_list',
|
||||
params,
|
||||
)
|
||||
|
||||
useApi<DomainTransferListResponse, DomainTransferListRequest>('/v1/domain/transfer/get_transfer_list', params)
|
||||
|
||||
// 自动续费
|
||||
export const fetchDomainAutoRenew = (params: DomainAutoRenewRequest) =>
|
||||
useApi<ApiResponse, DomainAutoRenewRequest>('/v1/domain/manage/set_domain_auto_renew', params)
|
||||
|
||||
|
||||
// 下载域名证书
|
||||
export const downloadDomainCertificate = (params: { domain_id: number }) =>
|
||||
useApi<ApiResponse, { domain_id: number }>('/v1/domain/manage/get_cert', params)
|
||||
@@ -128,39 +110,73 @@ export const privacyPrice = (params: PrivacyPriceRequest) =>
|
||||
|
||||
// 添加DNSSEC DS记录
|
||||
export const addDnssecDsRecord = (params: {
|
||||
domain_id: string
|
||||
key_tag: number
|
||||
alg: number
|
||||
digest_type: number
|
||||
digest: string
|
||||
}) =>
|
||||
useApi<ApiResponse, typeof params>('/v1/dns/dnssec/add_ds', params)
|
||||
domain_id: string
|
||||
key_tag: number
|
||||
alg: number
|
||||
digest_type: number
|
||||
digest: string
|
||||
}) => useApi<ApiResponse, typeof params>('/v1/dns/dnssec/add_ds', params)
|
||||
|
||||
// 获取DNSSEC DS记录列表
|
||||
export const getDnssecDsList = (params: {
|
||||
domain_id: string
|
||||
}) =>
|
||||
useApi<{
|
||||
code: number
|
||||
data: Array<{
|
||||
alg: number
|
||||
created_at: number
|
||||
digest: string
|
||||
digest_type: number
|
||||
id: number
|
||||
key_tag: number
|
||||
status: number
|
||||
updated_at: number
|
||||
}>
|
||||
msg: string
|
||||
status: boolean
|
||||
}, typeof params>('/v1/dns/dnssec/get_ds_list', params)
|
||||
export const getDnssecDsList = (params: { domain_id: string }) =>
|
||||
useApi<
|
||||
{
|
||||
code: number
|
||||
data: Array<{
|
||||
alg: number
|
||||
created_at: number
|
||||
digest: string
|
||||
digest_type: number
|
||||
id: number
|
||||
key_tag: number
|
||||
status: number
|
||||
updated_at: number
|
||||
}>
|
||||
msg: string
|
||||
status: boolean
|
||||
},
|
||||
typeof params
|
||||
>('/v1/dns/dnssec/get_ds_list', params)
|
||||
|
||||
// 删除DNSSEC DS记录
|
||||
export const deleteDnssecDsRecord = (params: {
|
||||
ds_id: number
|
||||
}) =>
|
||||
export const deleteDnssecDsRecord = (params: { ds_id: number }) =>
|
||||
useApi<ApiResponse, typeof params>('/v1/dns/dnssec/del_ds', params)
|
||||
|
||||
export const syncDnssecDsRecord = (params: { domain_id: string }) =>
|
||||
useApi<ApiResponse, typeof params>('/v1/dns/dnssec/sync_ds', params)
|
||||
|
||||
/**
|
||||
* 删除域名
|
||||
* @param params 删除域名请求参数
|
||||
* @returns 删除域名响应
|
||||
*/
|
||||
export const deleteDomain = (params: DeleteDomainRequest) => {
|
||||
return useApi<DeleteDomainApiResponse, DeleteDomainRequest>('/v1/domain/manage/delete_domain', params)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 修改隐私保护信息
|
||||
* @description 更新域名隐私保护后展示的邮箱地址
|
||||
* @param {UpdatePrivacyInfoRequest} params 修改参数,包含域名ID和新的邮箱地址
|
||||
* @returns {useAxiosReturn<UpdatePrivacyInfoApiResponse, UpdatePrivacyInfoRequest>} 返回修改结果
|
||||
*/
|
||||
export const updatePrivacyInfo = (params: UpdatePrivacyInfoRequest) =>
|
||||
useApi<UpdatePrivacyInfoApiResponse, UpdatePrivacyInfoRequest>('/v1/domain/privacy/update_privacy', params)
|
||||
|
||||
/**
|
||||
* @description 删除隐私保护信息
|
||||
* @description 关闭域名的隐私保护功能
|
||||
* @param {DeletePrivacyInfoRequest} params 删除参数,包含域名ID
|
||||
* @returns {useAxiosReturn<DeletePrivacyInfoApiResponse, DeletePrivacyInfoRequest>} 返回删除结果
|
||||
*/
|
||||
export const deletePrivacyInfo = (params: DeletePrivacyInfoRequest) =>
|
||||
useApi<DeletePrivacyInfoApiResponse, DeletePrivacyInfoRequest>('/v1/domain/privacy/del_privacy', params)
|
||||
|
||||
/**
|
||||
* @description 获取域名 whois 信息
|
||||
* @description 查询指定域名的 whois 注册信息
|
||||
* @param {GetWhoisInfoRequest} params 查询参数,包含完整域名
|
||||
* @returns {useAxiosReturn<GetWhoisInfoApiResponse, GetWhoisInfoRequest>} 返回 whois 信息
|
||||
*/
|
||||
export const getWhoisInfo = (params: GetWhoisInfoRequest) =>
|
||||
useApi<GetWhoisInfoApiResponse, GetWhoisInfoRequest>('/v1/domain/manage/get_whois_info', params)
|
||||
|
||||
@@ -28,7 +28,7 @@ const authMiddleware = requestMiddleware((config: AxiosRequestConfig) => {
|
||||
if (isDev()) {
|
||||
config.headers = {
|
||||
...config.headers,
|
||||
'X-UID': '1112',
|
||||
'X-UID': localStorage.getItem('x-uid') || '1112',
|
||||
}
|
||||
}
|
||||
return config
|
||||
|
||||
129
frontend/apps/domain-management-backend/src/api/security.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* @fileoverview 域名安全管理相关 API 接口
|
||||
* @description 提供安全状态查询、密保问题管理、敏感操作限制、全局转移锁等安全功能
|
||||
*/
|
||||
|
||||
import { useApi } from '@api/index'
|
||||
import type { ApiResponse } from '@/types/api'
|
||||
import type {
|
||||
SecurityStatusData,
|
||||
SecurityQuestionsListData,
|
||||
SecurityQuestionItem,
|
||||
SetupSecurityQuestionsRequest,
|
||||
UpdateProtectionSettingsRequest,
|
||||
VerifySecurityQuestionsRequest,
|
||||
VerifySecurityQuestionsResponse,
|
||||
SendPhoneCodeRequest,
|
||||
VerifyPhoneRequest,
|
||||
VerifyPhoneResponse,
|
||||
PanelWhitelistFormData,
|
||||
PanelWhitelistListRequest,
|
||||
PanelWhitelistItem,
|
||||
PanelWhitelistToggleRequest,
|
||||
PanelWhitelistDeleteRequest,
|
||||
} from '@/views/domain-security/types.d'
|
||||
|
||||
/**
|
||||
* @description 获取安全页面全局状态
|
||||
* @description 获取用户的安全设置状态,包括全局转移锁、密保问题、敏感操作限制等
|
||||
* @returns {useAxiosReturn<ApiResponse<SecurityStatusData>>} 返回安全状态数据
|
||||
*/
|
||||
export const getSecurityStatus = () =>
|
||||
useApi<ApiResponse<SecurityStatusData>>('/v1/user/security/get_security_status')
|
||||
|
||||
/**
|
||||
* @description 发送手机验证码
|
||||
* @description 向指定手机号发送验证码,用于手机号验证
|
||||
* @param {SendPhoneCodeRequest} params 发送验证码参数,包含手机号
|
||||
* @returns {useAxiosReturn<ApiResponse, SendPhoneCodeRequest>} 返回发送结果
|
||||
*/
|
||||
export const sendPhoneCode = (params: SendPhoneCodeRequest) =>
|
||||
useApi<ApiResponse, SendPhoneCodeRequest>('/v1/user/security/send_phone_code', params)
|
||||
|
||||
/**
|
||||
* @description 验证手机号
|
||||
* @description 验证手机号和验证码的正确性
|
||||
* @param {VerifyPhoneRequest} params 验证参数,包含手机号和验证码
|
||||
* @returns {useAxiosReturn<ApiResponse<VerifyPhoneResponse>, VerifyPhoneRequest>} 返回验证结果
|
||||
*/
|
||||
export const verifyPhone = (params: VerifyPhoneRequest) =>
|
||||
useApi<ApiResponse<VerifyPhoneResponse>, VerifyPhoneRequest>('/v1/user/security/verify_phone', params)
|
||||
|
||||
/**
|
||||
* @description 获取密保问题列表
|
||||
* @description 获取系统提供的所有密保问题选项
|
||||
* @returns {useAxiosReturn<ApiResponse<SecurityQuestionsListData>>} 返回密保问题列表
|
||||
*/
|
||||
export const getSecurityQuestionsList = () =>
|
||||
useApi<ApiResponse<SecurityQuestionsListData>>('/v1/user/security/get_security_questions_list')
|
||||
|
||||
/**
|
||||
* @description 设置密保问题
|
||||
* @description 设置或重置用户的密保问题和答案
|
||||
* @param {SetupSecurityQuestionsRequest} params 设置参数,包含问题ID和答案
|
||||
* @returns {useAxiosReturn<ApiResponse, SetupSecurityQuestionsRequest>} 返回设置结果
|
||||
*/
|
||||
export const setupSecurityQuestions = (params: SetupSecurityQuestionsRequest) =>
|
||||
useApi<ApiResponse, SetupSecurityQuestionsRequest>('/v1/user/security/setup_security_questions', params)
|
||||
|
||||
/**
|
||||
* @description 更新安全设置
|
||||
* @description 更新域名转移保护、DNS修改保护、敏感操作保护、全局转移锁等安全设置
|
||||
* @param {UpdateProtectionSettingsRequest} params 更新参数,包含各种保护开关
|
||||
* @returns {useAxiosReturn<ApiResponse, UpdateProtectionSettingsRequest>} 返回更新结果
|
||||
*/
|
||||
export const updateProtectionSettings = (params: UpdateProtectionSettingsRequest) =>
|
||||
useApi<ApiResponse, UpdateProtectionSettingsRequest>('/v1/user/security/update_protection_settings', params)
|
||||
|
||||
/**
|
||||
* @description 获取验证用的密保问题
|
||||
* @description 获取用户设置的密保问题,用于验证身份
|
||||
* @returns {useAxiosReturn<ApiResponse<{ questions: SecurityQuestionItem[] }>>} 返回验证用密保问题
|
||||
*/
|
||||
export const getSecurityQuestionsForVerification = () =>
|
||||
useApi<ApiResponse<{ questions: SecurityQuestionItem[] }>>('/v1/user/security/get_security_questions_for_verification')
|
||||
|
||||
/**
|
||||
* @description 验证密保问题答案
|
||||
* @description 验证用户提供的密保问题答案是否正确
|
||||
* @param {VerifySecurityQuestionsRequest} params 验证参数,包含操作类型和答案
|
||||
* @returns {useAxiosReturn<ApiResponse<VerifySecurityQuestionsResponse>, VerifySecurityQuestionsRequest>} 返回验证结果和安全令牌
|
||||
*/
|
||||
export const verifySecurityQuestions = (params: VerifySecurityQuestionsRequest) =>
|
||||
useApi<ApiResponse<VerifySecurityQuestionsResponse>, VerifySecurityQuestionsRequest>('/v1/user/security/verify_security_questions', params)
|
||||
|
||||
/**
|
||||
* @description 设置面板IP白名单
|
||||
* @description 创建或更新面板IP白名单设置
|
||||
* @param {PanelWhitelistFormData} params 白名单参数,包含IP列表、名称、状态和备注
|
||||
* @returns {useAxiosReturn<ApiResponse, PanelWhitelistFormData>} 返回设置结果
|
||||
*/
|
||||
export const setPanelWhitelist = (params: PanelWhitelistFormData) =>
|
||||
useApi<ApiResponse, PanelWhitelistFormData>('/v1/user/security/set_panel_whitelist', params)
|
||||
|
||||
/**
|
||||
* @description 获取面板IP白名单列表
|
||||
* @description 获取用户的面板IP白名单列表
|
||||
* @param {PanelWhitelistListRequest} params 查询参数,包含分页、搜索条件等
|
||||
* @returns {useAxiosReturn<ApiResponse<{ list: PanelWhitelistItem[], total: number, page: number, limit: number }>, PanelWhitelistListRequest>} 返回白名单列表
|
||||
*/
|
||||
export const getPanelWhitelist = (params?: PanelWhitelistListRequest) =>
|
||||
useApi<ApiResponse<{ list: PanelWhitelistItem[], total: number, page: number, limit: number }>, PanelWhitelistListRequest>('/v1/user/security/get_panel_whitelist', params)
|
||||
|
||||
/**
|
||||
* @description 切换面板IP白名单启用状态
|
||||
* @description 切换指定IP白名单的启用/禁用状态
|
||||
* @param {PanelWhitelistToggleRequest} params 切换参数,包含ID和启用状态
|
||||
* @returns {useAxiosReturn<ApiResponse, PanelWhitelistToggleRequest>} 返回切换结果
|
||||
*/
|
||||
export const togglePanelWhitelist = (params: PanelWhitelistToggleRequest) =>
|
||||
useApi<ApiResponse, PanelWhitelistToggleRequest>('/v1/user/security/toggle_panel_whitelist', params)
|
||||
|
||||
/**
|
||||
* @description 删除面板IP白名单
|
||||
* @description 删除指定的IP白名单记录
|
||||
* @param {PanelWhitelistDeleteRequest} params 删除参数,包含ID
|
||||
* @returns {useAxiosReturn<ApiResponse, PanelWhitelistDeleteRequest>} 返回删除结果
|
||||
*/
|
||||
export const deletePanelWhitelist = (params: PanelWhitelistDeleteRequest) =>
|
||||
useApi<ApiResponse, PanelWhitelistDeleteRequest>('/v1/user/security/delete_panel_whitelist', params)
|
||||
@@ -3,6 +3,22 @@ import type { ApiResponse } from '@/types/api'
|
||||
import type {
|
||||
CreateTransferOrderRequest,
|
||||
CreateTransferOrderResponse,
|
||||
DomainTransferOutListRequest,
|
||||
DomainTransferOutListResponse,
|
||||
CancelTransferOutRequest,
|
||||
CancelTransferOutResponse,
|
||||
ApproveTransferOutRequest,
|
||||
ApproveTransferOutResponse,
|
||||
SendTransferCodeRequest,
|
||||
SendTransferCodeResponse,
|
||||
BtAccountTransferListRequest,
|
||||
BtAccountTransferListResponse,
|
||||
BtAccountTransferRequest,
|
||||
BtAccountTransferResponse,
|
||||
InsideTransferRequest,
|
||||
InsideTransferResponse,
|
||||
InsideTransferCancelRequest,
|
||||
InsideTransferCancelResponse,
|
||||
} from '@/types/transfer'
|
||||
|
||||
export const createTransferOrder = (params: CreateTransferOrderRequest) =>
|
||||
@@ -10,4 +26,70 @@ export const createTransferOrder = (params: CreateTransferOrderRequest) =>
|
||||
|
||||
// 取消转入
|
||||
export const cancelTransfer = (params: { record_id: number }) =>
|
||||
useApi<ApiResponse, { record_id: number }>('/v1/domain/transfer/cancel_transfer', params)
|
||||
useApi<ApiResponse, { record_id: number }>('/v1/domain/transfer/cancel_transfer', params)
|
||||
|
||||
// 获取域名转出列表
|
||||
export const fetchDomainTransferOutList = (params: DomainTransferOutListRequest) =>
|
||||
useApi<DomainTransferOutListResponse, DomainTransferOutListRequest>('/v1/domain/transfer/get_transfer_list', {...params,transfer_type:2})
|
||||
|
||||
// -------------------- 域名转出操作接口 --------------------
|
||||
|
||||
/**
|
||||
* 取消域名转出
|
||||
* @param params 取消转出请求参数
|
||||
* @returns 取消转出响应
|
||||
*/
|
||||
export const cancelDomainTransferOut = (params: CancelTransferOutRequest) =>
|
||||
useApi<CancelTransferOutResponse, CancelTransferOutRequest>('/v1/domain/transfer/outside_transfer_canel', params)
|
||||
|
||||
/**
|
||||
* 同意域名转出
|
||||
* @param params 同意转出请求参数
|
||||
* @returns 同意转出响应
|
||||
*/
|
||||
export const approveDomainTransferOut = (params: ApproveTransferOutRequest) =>
|
||||
useApi<ApproveTransferOutResponse, ApproveTransferOutRequest>('/v1/domain/transfer/outside_transfer_approve', params)
|
||||
|
||||
/**
|
||||
* 申请域名转出
|
||||
* @param params 发送转移码请求参数
|
||||
* @returns 发送转移码响应
|
||||
*/
|
||||
export const sendDomainTransferCode = (params: SendTransferCodeRequest) =>
|
||||
useApi<SendTransferCodeResponse, SendTransferCodeRequest>('/v1/domain/transfer/outside_transfer', params)
|
||||
|
||||
// -------------------- 堡塔账号转入相关接口 --------------------
|
||||
|
||||
/**
|
||||
* 获取堡塔账号转入列表
|
||||
* @param params 查询参数
|
||||
* @returns 堡塔账号转入列表响应
|
||||
*/
|
||||
export const fetchBtAccountTransferList = (params: BtAccountTransferListRequest) =>
|
||||
useApi<BtAccountTransferListResponse, BtAccountTransferListRequest>('/v1/domain/transfer/get_transfer_list', { ...params, transfer_type: 1 })
|
||||
|
||||
/**
|
||||
* 执行堡塔账号转入操作
|
||||
* @param params 堡塔账号转入请求参数
|
||||
* @returns 堡塔账号转入响应
|
||||
*/
|
||||
export const executeBtAccountTransfer = (params: BtAccountTransferRequest) =>
|
||||
useApi<BtAccountTransferResponse, BtAccountTransferRequest>('/v1/domain/transfer/inside_transfer_approve', params)
|
||||
|
||||
// -------------------- 域名内部转移相关接口 --------------------
|
||||
|
||||
/**
|
||||
* 申请域名内部转移
|
||||
* @param params 申请转移请求参数
|
||||
* @returns 申请转移响应
|
||||
*/
|
||||
export const applyInsideTransfer = (params: InsideTransferRequest) =>
|
||||
useApi<InsideTransferResponse, InsideTransferRequest>('/v1/domain/transfer/inside_transfer', params)
|
||||
|
||||
/**
|
||||
* 取消域名内部转移
|
||||
* @param params 取消转移请求参数
|
||||
* @returns 取消转移响应
|
||||
*/
|
||||
export const cancelInsideTransfer = (params: InsideTransferCancelRequest) =>
|
||||
useApi<InsideTransferCancelResponse, InsideTransferCancelRequest>('/v1/domain/transfer/inside_transfer_canel', params)
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* 安全验证问题组件
|
||||
* 用于验证用户的密保问题答案
|
||||
*/
|
||||
import { defineComponent, ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { NForm, NFormItem, NInput, NButton, NSpace, NText } from 'naive-ui'
|
||||
import { useMessage } from '@baota/naive-ui/hooks'
|
||||
import { verifySecurityQuestions } from '@/api/security'
|
||||
import { useDomainSecurityState } from '@/views/domain-security/useStore'
|
||||
import type { SecurityQuestionItem, VerifySecurityQuestionsResponse } from '@/views/domain-security/types.d'
|
||||
import { closeQuestionsModal } from '@/public/dialog'
|
||||
|
||||
// Props 接口定义
|
||||
interface Props {
|
||||
/** 密保问题数组,格式为[{question_id: 3, question_text: "您的出生地是哪里?"}] */
|
||||
questions: SecurityQuestionItem[]
|
||||
/** 操作类型,用于接口调用,默认为 'dns_modify' */
|
||||
operationType?: string
|
||||
/** 验证成功回调,传递security_token */
|
||||
onSuccess?: (securityToken: string) => void
|
||||
/** 取消回调 */
|
||||
onCancel?: () => void
|
||||
}
|
||||
const message = useMessage()
|
||||
|
||||
export default defineComponent({
|
||||
name: 'VerifySecurityQuestions',
|
||||
props: {
|
||||
questions: {
|
||||
type: Array as () => SecurityQuestionItem[],
|
||||
required: true,
|
||||
},
|
||||
operationType: {
|
||||
type: String,
|
||||
default: 'operation_protection',
|
||||
},
|
||||
onSuccess: {
|
||||
type: Function as unknown as () => (securityToken: string) => void,
|
||||
default: () => () => {},
|
||||
},
|
||||
onCancel: {
|
||||
type: Function as unknown as () => () => void,
|
||||
default: () => () => {},
|
||||
},
|
||||
},
|
||||
setup(props: Props) {
|
||||
// 组合式API
|
||||
const router = useRouter()
|
||||
const { activeTab } = useDomainSecurityState()
|
||||
|
||||
// 响应式数据
|
||||
const formRef = ref()
|
||||
const loading = ref(false)
|
||||
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
answer: '',
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = {
|
||||
answer: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入密保答案',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
{
|
||||
min: 1,
|
||||
max: 100,
|
||||
message: '密保答案长度应在1-100个字符之间',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// 计算属性:获取第一个密保问题
|
||||
const currentQuestion = computed(() => {
|
||||
return props.questions && props.questions.length > 0 ? props.questions[0] : null
|
||||
})
|
||||
|
||||
// 计算属性:密保问题文本
|
||||
const questionText = computed(() => {
|
||||
return currentQuestion.value?.question_text || '暂无密保问题'
|
||||
})
|
||||
|
||||
// 方法:处理取消操作
|
||||
const handleCancel = () => {
|
||||
// 优先使用传入的onCancel回调,否则使用默认的关闭弹窗方法
|
||||
if (props.onCancel) {
|
||||
props.onCancel()
|
||||
} else {
|
||||
closeQuestionsModal()
|
||||
}
|
||||
}
|
||||
|
||||
// 方法:处理确认操作
|
||||
const handleConfirm = async () => {
|
||||
// 表单验证
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否有密保问题
|
||||
if (!currentQuestion.value || !currentQuestion.value.question_id) {
|
||||
message.error('没有有效的密保问题')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
// 调用验证接口
|
||||
const { fetch, data, message } = verifySecurityQuestions({
|
||||
operation_type: props.operationType || 'operation_protection',
|
||||
answers: [
|
||||
{
|
||||
question_id: currentQuestion.value.question_id,
|
||||
answer: formData.value.answer.trim(),
|
||||
},
|
||||
],
|
||||
})
|
||||
message.value = true
|
||||
await fetch()
|
||||
|
||||
if (data.value?.status) {
|
||||
handleCancel()
|
||||
const response = data.value.data as VerifySecurityQuestionsResponse
|
||||
|
||||
// 清空表单
|
||||
formData.value.answer = ''
|
||||
|
||||
// 通过回调函数将security_token传递给上层方法
|
||||
props.onSuccess?.(response.security_token)
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 方法:处理密保重置页面跳转
|
||||
const handleResetRedirect = () => {
|
||||
closeQuestionsModal()
|
||||
message.info('准备跳转到密保重置页面')
|
||||
// 切换到 basic-security 标签页
|
||||
activeTab.value = 'basic-security'
|
||||
|
||||
// 3秒后自动跳转
|
||||
setTimeout(() => {
|
||||
router.push('/domain-security')
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
// 组件挂载时的初始化
|
||||
onMounted(() => {
|
||||
// 检查是否有密保问题数据
|
||||
if (!props.questions || props.questions.length === 0) {
|
||||
message.warning('未提供密保问题数据')
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
formRef,
|
||||
loading,
|
||||
formData,
|
||||
formRules,
|
||||
currentQuestion,
|
||||
questionText,
|
||||
handleCancel,
|
||||
handleConfirm,
|
||||
handleResetRedirect,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{/* 表单区域 */}
|
||||
<NForm ref="formRef" model={this.formData} rules={this.formRules} labelPlacement="top">
|
||||
{/* 密保问题显示区 */}
|
||||
<NFormItem label="密保问题" required>
|
||||
<NInput v-model:value={this.questionText} readonly placeholder="密保问题" />
|
||||
</NFormItem>
|
||||
|
||||
{/* 密保答案输入区 */}
|
||||
<NFormItem label="密保答案" path="answer" required>
|
||||
<NInput
|
||||
v-model:value={this.formData.answer}
|
||||
placeholder="请输入密保答案"
|
||||
type="password"
|
||||
showPasswordOn="click"
|
||||
disabled={this.loading}
|
||||
onKeyup={(e: KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.handleConfirm()
|
||||
}
|
||||
}}
|
||||
clearable
|
||||
/>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
|
||||
{/* 辅助功能区 */}
|
||||
<div>
|
||||
<NText depth={3}>忘记密保答案?请联系客服或前往</NText>
|
||||
<NButton type="primary" text size="small" onClick={this.handleResetRedirect} disabled={this.loading}>
|
||||
密保重置页面
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮区 */}
|
||||
<NSpace justify="end">
|
||||
<NButton onClick={this.handleCancel} disabled={this.loading}>
|
||||
取消
|
||||
</NButton>
|
||||
<NButton type="primary" onClick={this.handleConfirm} loading={this.loading}>
|
||||
确认
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
144
frontend/apps/domain-management-backend/src/public/dialog.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
import { ref } from 'vue'
|
||||
import { useModal } from '@baota/naive-ui/hooks'
|
||||
import type { SecurityQuestionItem } from '@/views/domain-security/types.d'
|
||||
import VerifySecurityQuestions from '@/components/global/VerifySecurityQuestions'
|
||||
import { updateDnsRecord } from '@/api/dns'
|
||||
import type { UpdateDnsRecordRequest } from '@/types/dns'
|
||||
import { useError } from '@baota/hooks/error'
|
||||
|
||||
const questionsModal = ref<any>()
|
||||
const { handleError } = useError()
|
||||
|
||||
export function closeQuestionsModal() {
|
||||
if (questionsModal.value) {
|
||||
questionsModal.value.close()
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 打开问题验证弹窗
|
||||
* @param questions 密保问题列表
|
||||
* @param onSuccess 验证成功回调函数,接收security_token参数
|
||||
* @param operationType 操作类型,默认为'operation_protection'
|
||||
*/
|
||||
export function openSecurityQuestionsModal(
|
||||
questions: SecurityQuestionItem | SecurityQuestionItem[],
|
||||
onSuccess?: (securityToken: string) => void,
|
||||
operationType?: string,
|
||||
) {
|
||||
// 确保 questions 是数组格式
|
||||
const questionList = Array.isArray(questions) ? questions : [questions]
|
||||
|
||||
// useModal 直接返回 modal 实例,不需要赋值给 ref
|
||||
questionsModal.value = useModal({
|
||||
title: '验证密保问题',
|
||||
area: 520,
|
||||
component: VerifySecurityQuestions,
|
||||
componentProps: {
|
||||
questions: questionList,
|
||||
operationType: operationType || 'operation_protection',
|
||||
onSuccess: onSuccess,
|
||||
onCancel: () => {
|
||||
closeQuestionsModal()
|
||||
},
|
||||
},
|
||||
footer: false,
|
||||
onClose: () => {
|
||||
questionsModal.value.close()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的API请求封装,支持密保验证流程
|
||||
* @param apiFunction API函数,返回包含fetch、data、message、setConfig的对象
|
||||
* @param params API请求参数
|
||||
* @param options 配置选项
|
||||
* @returns Promise<any> 请求结果
|
||||
*/
|
||||
export async function executeApiWithSecurityVerification<T = any, P = any>(
|
||||
apiFunction: (params: P) => {
|
||||
fetch: () => Promise<void>
|
||||
data: { value: any }
|
||||
message: { value: boolean }
|
||||
setConfig: (config: any) => void
|
||||
},
|
||||
params: P,
|
||||
options: {
|
||||
/** 是否显示成功消息 */
|
||||
showMessage?: boolean
|
||||
/** 操作类型,用于密保验证 */
|
||||
operationType?: string
|
||||
/** 加载状态控制函数 */
|
||||
setLoading?: (loading: boolean) => void
|
||||
/** 自定义错误处理函数 */
|
||||
onError?: (error: any) => void
|
||||
/** 需要密保验证的错误码,默认为3005 */
|
||||
securityVerificationCode?: number
|
||||
} = {},
|
||||
): Promise<T> {
|
||||
const {
|
||||
showMessage = true,
|
||||
operationType = 'operation_protection',
|
||||
setLoading,
|
||||
onError,
|
||||
securityVerificationCode = 3005,
|
||||
} = options
|
||||
|
||||
// 设置加载状态
|
||||
setLoading?.(true)
|
||||
|
||||
try {
|
||||
const { fetch, data, message, setConfig } = apiFunction(params)
|
||||
|
||||
// 设置是否显示消息
|
||||
message.value = showMessage
|
||||
|
||||
// 发起初始请求
|
||||
await fetch()
|
||||
console.log(data.value, 'executeApiWithSecurityVerification')
|
||||
// 检查是否需要密保验证
|
||||
if (data.value?.code === securityVerificationCode) {
|
||||
// 返回Promise,等待密保验证完成
|
||||
return new Promise((resolve, reject) => {
|
||||
// 打开密保验证弹窗,传入成功回调
|
||||
openSecurityQuestionsModal(
|
||||
data.value?.data?.questions,
|
||||
async (securityToken: string) => {
|
||||
try {
|
||||
console.log('收到security_token:', securityToken)
|
||||
// 使用获取到的token更新请求头配置
|
||||
setConfig({
|
||||
headers: {
|
||||
'Security-Token': securityToken,
|
||||
},
|
||||
})
|
||||
// 重新发起请求
|
||||
await fetch()
|
||||
resolve(data.value)
|
||||
} catch (error) {
|
||||
if (onError) {
|
||||
onError(error)
|
||||
} else {
|
||||
handleError(error)
|
||||
}
|
||||
reject(error)
|
||||
}
|
||||
},
|
||||
operationType,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return data.value
|
||||
} catch (err) {
|
||||
if (onError) {
|
||||
onError(err)
|
||||
} else {
|
||||
handleError(err)
|
||||
}
|
||||
return false as T
|
||||
} finally {
|
||||
// 重置加载状态
|
||||
setLoading?.(false)
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ const routes: RouteRecordRaw[] = [
|
||||
name: 'DomainTransfer',
|
||||
component: DomainTransfer,
|
||||
meta: {
|
||||
title: '域名转入',
|
||||
title: '域名转入转出',
|
||||
icon: 'transfer',
|
||||
requiresAuth: true,
|
||||
},
|
||||
|
||||
@@ -47,6 +47,8 @@ export interface DomainItem {
|
||||
status: number
|
||||
/** 域名后缀,例如 com/net */
|
||||
suffix: string
|
||||
/** 域名锁(0未锁定 可以操作 1已锁定 无法操作) */
|
||||
transfer_lock: number
|
||||
}
|
||||
|
||||
export interface DomainListData {
|
||||
@@ -176,6 +178,11 @@ export interface DomainInfo {
|
||||
suffix: string
|
||||
/** 转移锁(1 锁定,0 解锁) */
|
||||
transfer_lock: number
|
||||
/** 转移锁状态 */
|
||||
transfer_status: {
|
||||
msg: string
|
||||
status: number //(0 禁止转移 1 可以转移)
|
||||
}
|
||||
/** 用户 ID */
|
||||
uid: number
|
||||
/** 更新锁(1 锁定,0 解锁) */
|
||||
@@ -248,7 +255,6 @@ export interface RealNameInfo {
|
||||
verify_time: string
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 域名详情响应数据
|
||||
*/
|
||||
@@ -257,6 +263,10 @@ export interface DomainDetailData {
|
||||
dns_records: DnsRecordItem[]
|
||||
/** 域名详情信息 */
|
||||
domain_info: DomainInfo
|
||||
/** 内部转移状态 */
|
||||
inside_transfer_status: null | { domain: string; status: number; to_account: string; transfer_code: string }
|
||||
/** 外部转移状态 */
|
||||
outside_transfer_status: null | number
|
||||
/** 实名模板信息 */
|
||||
real_name_info: RealNameInfo
|
||||
/** 实名信息更新状态 */
|
||||
@@ -391,33 +401,31 @@ export interface DomainPriceQueryRequest {
|
||||
}
|
||||
|
||||
export interface DomainPriceResult {
|
||||
domain: string;
|
||||
year: number;
|
||||
/** 原价(字符串金额) */
|
||||
price: string;
|
||||
/** 折扣价(字符串金额) */
|
||||
discount_price: string;
|
||||
/** 节省金额(字符串金额) */
|
||||
savings: string;
|
||||
/** 失败信息(可选) */
|
||||
error?: string;
|
||||
domain: string
|
||||
year: number
|
||||
/** 原价(字符串金额) */
|
||||
price: string
|
||||
/** 折扣价(字符串金额) */
|
||||
discount_price: string
|
||||
/** 节省金额(字符串金额) */
|
||||
savings: string
|
||||
/** 失败信息(可选) */
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface DomainPriceQueryData {
|
||||
query_time: string;
|
||||
year: number;
|
||||
results: DomainPriceResult[];
|
||||
query_time: string
|
||||
year: number
|
||||
results: DomainPriceResult[]
|
||||
}
|
||||
|
||||
export type DomainPriceQueryResponse = ApiResponse<DomainPriceQueryData>;
|
||||
|
||||
export type DomainPriceQueryResponse = ApiResponse<DomainPriceQueryData>
|
||||
|
||||
export type DomainAutoRenewRequest = {
|
||||
domain_id: number
|
||||
status: 0 | 1
|
||||
}
|
||||
|
||||
|
||||
export interface DomainRealNameUpdateRequest {
|
||||
/** 域名 ID */
|
||||
domain_id: number
|
||||
@@ -453,8 +461,28 @@ export interface PrivacyData {
|
||||
export type PrivacyResponse = ApiResponse<PrivacyRequest>
|
||||
|
||||
export interface PrivacyPriceRequest {
|
||||
/** 1:新购 2:续费 */
|
||||
/** 隐私保护类型 */
|
||||
type: number
|
||||
/** 年份 */
|
||||
/** 年限 */
|
||||
year: number
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除域名请求参数
|
||||
*/
|
||||
export interface DeleteDomainRequest {
|
||||
/** 域名ID */
|
||||
id: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除域名响应数据
|
||||
*/
|
||||
export interface DeleteDomainResponse {
|
||||
/** 删除结果 */
|
||||
success: boolean
|
||||
/** 响应消息 */
|
||||
message?: string
|
||||
}
|
||||
|
||||
export type DeleteDomainApiResponse = ApiResponse<DeleteDomainResponse>
|
||||
|
||||
@@ -127,6 +127,8 @@ export interface CreateContactRequest {
|
||||
id_type: number
|
||||
/** 证件号 */
|
||||
id_number: string
|
||||
/** 企业联系人证件号码 */
|
||||
business_concat_id_number?: string
|
||||
/** 证件照(正面 Base64) */
|
||||
id_image_front: string
|
||||
/** 证件照(背面 Base64) */
|
||||
|
||||