1<!DOCTYPE html> 2<html lang="en"> 3 4<head> 5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>{{ case.name }}</title> 8 <style> 9 /* Execution Log style Begin */ 10 a:link { 11 text-decoration: none; 12 } 13 14 #logs div { 15 white-space: pre-wrap; 16 font-size: 14px; 17 } 18 19 .aw, 20 .step { 21 font-weight: bold; 22 color: #1E90FF; 23 } 24 25 .aw1 { 26 color: #008B45; 27 } 28 29 .aw2 { 30 color: #00CD00; 31 } 32 33 .aw3 { 34 font-weight: bold; 35 background: linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet); 36 -webkit-background-clip: text; 37 color: transparent; 38 display: inline-block; 39 background-repeat: no-repeat; 40 } 41 42 .aw3-container { 43 display: block; /* 确保容器是块级元素 */ 44 overflow: hidden; /* 确保背景渐变效果 */ 45 } 46 47 .ui_container { 48 background-color: black; 49 color: #18b302; 50 padding: 20px; 51 box-sizing: border-box; 52 } 53 54 .ui_component-number { 55 color: #18b302; 56 } 57 58 .ui_components { 59 display: flex; 60 flex-wrap: wrap; 61 } 62 63 .ui_component { 64 flex: 1; 65 margin: 5px; 66 color: #18b302; 67 background-color: #333; 68 padding: 10px; 69 box-sizing: border-box; 70 overflow-wrap: break-word; /* Ensures long words break */ 71 word-break: break-all; /* Ensures long words break */ 72 } 73 74 .ui_component pre { 75 white-space: pre-wrap; 76 word-wrap: break-word; /* Ensures long words break */ 77 } 78 79 .ui_old-component .highlight { 80 color: red; 81 } 82 .ui_new-component .highlight { 83 color: yellow; 84 } 85 86 @media (max-width: 600px) { 87 .ui_components { 88 flex-direction: column; 89 } 90 } 91 92 .error, 93 .blocked, 94 .fail, 95 .failed, 96 .unavailable { 97 color: red !important; 98 } 99 100 .pass, 101 .passed { 102 color: green !important; 103 } 104 105 .warn, 106 .warning { 107 color: #FFCC00; 108 } 109 /* Execution Log style End */ 110 111 body { 112 margin: 0 100px; 113 background-color: #fff; 114 } 115 116 h4 { 117 margin-top: 16px; 118 margin-bottom: 16px; 119 } 120 121 .el-descriptions__header { 122 display: flex; 123 justify-content: space-between; 124 align-items: center; 125 margin-bottom: 16px; 126 } 127 128 .el-descriptions__title { 129 color: #000000; 130 font-size: 16px; 131 font-weight: 700; 132 } 133 134 .el-descriptions__body { 135 background-color: #ffffff; 136 max-height: 121px; 137 overflow-y: auto; 138 } 139 140 .el-descriptions__body .el-descriptions__table { 141 border-collapse: collapse; 142 width: 100%; 143 } 144 145 .el-descriptions__body .el-descriptions__table.is-bordered .el-descriptions__cell { 146 border: 1px solid #edeef5; 147 padding: 8px 11px; 148 } 149 150 .el-descriptions__label.el-descriptions__cell.is-bordered-label { 151 font-weight: 700; 152 color: #606266; 153 background: #f5f7fa; 154 } 155 156 .el-descriptions__body .el-descriptions__table .el-descriptions__cell { 157 box-sizing: border-box; 158 text-align: left; 159 line-height: 23px; 160 font-size: 14px; 161 } 162 163 .el-descriptions__table .el-descriptions__cell { 164 word-wrap: break-word; 165 word-break: break-all; 166 } 167 168 .header .el-descriptions__table .el-descriptions__cell { 169 width: calc(100%/6); 170 } 171 172 .toolbar .el-descriptions__table .el-descriptions__label { 173 width: 12.5%; 174 } 175 176 .el-descriptions__content.el-descriptions__cell.is-bordered-content { 177 color: #303133; 178 } 179 180 ::-webkit-scrollbar { 181 width: 8px; 182 height: 8px; 183 border-radius: 8px; 184 background-color: #bcbcbc; 185 } 186 187 ::-webkit-scrollbar-track { 188 border-radius: 8px; 189 background-color: #f5f5f5; 190 } 191 192 ::-webkit-scrollbar-thumb { 193 border-radius: 8px; 194 background-color: #bfc3c6; 195 } 196 197 .el-popover.el-popper { 198 min-width: 280px; 199 } 200 201 .el-progress-bar__innerText { 202 color: #37383a; 203 } 204 205 .el-descriptions__title { 206 color: #000000; 207 } 208 209 .el-table__body .el-table__cell, 210 .el-table__header-wrapper .el-table__cell { 211 border-left: none; 212 border-right: none; 213 } 214 215 .el-table--border .el-table__inner-wrapper::after, 216 .el-table__border-left-patch, 217 .el-table--border::after, 218 .el-table--border::before { 219 display: none; 220 } 221 222 .toolbar { 223 display: flex; 224 border: 1px solid #ebeef5; 225 max-height: 760px; 226 } 227 228 .toolbar ul { 229 min-width: 25%; 230 height: auto; 231 overflow-y: auto; 232 flex-direction: column; 233 justify-content: flex-start; 234 flex-wrap: nowrap; 235 padding: 0; 236 padding-top: 0; 237 margin-top: 0; 238 margin-bottom: 0; 239 border-right: 1px solid #ebeef5; 240 } 241 242 .toolbar ul li { 243 height: 60px; 244 display: flex; 245 flex-direction: column; 246 justify-content: center; 247 padding-left: 12px; 248 cursor: pointer; 249 border-bottom: 1px solid #ebeef5; 250 background: #f5f7fa; 251 } 252 253 .toolbar ul li .li-div { 254 display: flex; 255 justify-content: space-between; 256 } 257 258 .toolbar ul li .li-span { 259 font-size: 14px; 260 display: -webkit-box; 261 overflow: hidden; 262 text-overflow: ellipsis; 263 max-width: 300px; 264 line-height: 23px; 265 white-space: normal; 266 word-break: break-all; 267 -webkit-box-orient: vertical; 268 -webkit-line-clamp: 2; 269 } 270 271 .toolbar ul li .li-tag { 272 margin-left: 10px; 273 font-size: 14px; 274 } 275 276 .toolbar ul li svg { 277 margin-right: 10px; 278 margin-left: 10px; 279 width: 16px; 280 height: 16px 281 } 282 283 .toolbar div img { 284 height: 514px; 285 } 286 287 .toolbar ul .isChecked { 288 background: #c9e1f8; 289 } 290 291 .toolbar ul .isPassed { 292 color: red; 293 } 294 295 .toolbar .img-div { 296 max-height: 630px; 297 overflow-y: auto; 298 display: flex; 299 justify-content: start; 300 margin-top: 10px; 301 302 } 303 304 .toolbar .screen-div { 305 min-width: 420px 306 } 307 308 .div-error { 309 white-space: nowrap; 310 overflow: hidden; 311 text-overflow: ellipsis; 312 } 313 314 .div-others { 315 display: inline-block; 316 width: 90%; 317 line-height: 23px; 318 vertical-align: sub 319 } 320 321 .span-img { 322 width: 5%; 323 display: inline-block; 324 } 325 326 .img { 327 margin-left: 5px; 328 cursor: pointer; 329 height: 16px; 330 width: 16px; 331 } 332 333 .div-dialog { 334 border-bottom: 1px solid #ebeef5; 335 border-top: 1px solid #ebeef5; 336 } 337 338 .des-style { 339 max-height: 342px !important; 340 overflow: auto; 341 } 342 343 .div-dialog .el-descriptions__table tr:nth-child(1) .el-descriptions__cell { 344 border-top: none; 345 } 346 347 .div-dialog .el-descriptions__table tr:last-child .el-descriptions__cell { 348 border-bottom: none; 349 } 350 351 .el-dialog__body { 352 padding-top: 10px; 353 padding-bottom: 40px; 354 } 355 356 .el-dialog__headerbtn { 357 top: -2px; 358 } 359 360 .el-table--fit { 361 border-right: 0; 362 border-bottom: 0; 363 } 364 365 .el-table { 366 position: relative; 367 overflow: hidden; 368 box-sizing: border-box; 369 height: fit-content; 370 width: 100%; 371 max-width: 100%; 372 background-color: #ffffff; 373 font-size: 14px; 374 color: #606266; 375 } 376 377 .el-table__inner-wrapper { 378 position: relative; 379 display: flex; 380 flex-direction: column; 381 height: 100%; 382 } 383 384 .el-table__header-wrapper { 385 overflow: hidden; 386 flex-shrink: 0; 387 width: 100%; 388 } 389 390 .el-table__header { 391 table-layout: fixed; 392 border-collapse: separate; 393 } 394 395 .el-table th.el-table__cell.is-leaf { 396 border-bottom: 1px solid #ebeef5; 397 } 398 399 .el-table th.el-table__cell { 400 -webkit-user-select: none; 401 -moz-user-select: none; 402 -ms-user-select: none; 403 user-select: none; 404 background-color: #ffffff; 405 border-left: none; 406 border-right: none; 407 } 408 409 .el-table .el-table__cell { 410 padding: 8px 0; 411 min-width: 0; 412 box-sizing: border-box; 413 text-overflow: ellipsis; 414 vertical-align: middle; 415 position: relative; 416 text-align: left; 417 z-index: 1; 418 } 419 420 .el-table__body-wrapper { 421 overflow: hidden; 422 position: relative; 423 flex: 1; 424 width: 100%; 425 } 426 427 .el-table__body { 428 table-layout: fixed; 429 border-collapse: separate; 430 } 431 432 .el-table tr { 433 background-color: #ffffff; 434 } 435 436 .el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell { 437 background-color: #f5f7fa; 438 } 439 440 .el-table--enable-row-transition .el-table__body td.el-table__cell { 441 transition: background-color .25s ease; 442 } 443 444 .el-table td.el-table__cell, 445 .el-table th.el-table__cell.is-leaf { 446 border-bottom: 1px solid #ebeef5; 447 } 448 449 .el-table thead { 450 color: #909399; 451 font-weight: 500; 452 } 453 454 .el-table .cell { 455 box-sizing: border-box; 456 overflow: hidden; 457 text-overflow: ellipsis; 458 white-space: normal; 459 word-break: break-all; 460 line-height: 23px; 461 padding: 0 12px; 462 } 463 464 .el-scrollbar { 465 overflow: hidden; 466 position: relative; 467 height: 100%; 468 } 469 470 .el-scrollbar__wrap--hidden-default { 471 scrollbar-width: none; 472 } 473 474 .el-scrollbar__wrap { 475 overflow: auto; 476 height: 100%; 477 } 478 479 .el-table .cell.el-tooltip { 480 white-space: nowrap; 481 overflow: hidden; 482 text-overflow: ellipsis; 483 min-width: 50px; 484 } 485 486 .mask { 487 background: rgba(0, 0, 0, 0.5); 488 width: 100%; 489 height: 100%; 490 position: fixed; 491 left: 0; 492 z-index: 20001; 493 top: 0; 494 display: none; 495 justify-content: center; 496 align-items: center; 497 } 498 499 .mask-imgBg { 500 background-color: #fff; 501 position: relative; 502 border-radius: 0.1875rem; 503 border: 0.3125rem solid #fff; 504 } 505 506 .mask-delete { 507 position: absolute; 508 z-index: 5; 509 right: 18px; 510 top: 20px; 511 transform-origin: center center; 512 width: 50px; 513 height: 50px; 514 border-radius: 50%; 515 background-color: #f0f0f0; 516 text-align: center; 517 line-height: 50px; 518 font-weight: 700; 519 box-shadow: 0 0 6px rgba(0, 0, 0, .2); 520 cursor: pointer; 521 } 522 523 .mask-delete:hover { 524 cursor: pointer; 525 } 526 527 .svg-style { 528 width: 28px; 529 height: 28px; 530 cursor: pointer; 531 } 532 533 .btn-div { 534 margin-bottom: 5px; 535 border-bottom: 1px solid #d1cdcd; 536 padding: 5px; 537 } 538 539 .scroll-top-btn { 540 padding: 5px; 541 } 542 543 .table-margin { 544 margin-top: 10px; 545 } 546 </style> 547</head> 548 549<body> 550 <div id="app"> 551 <h3>{{ case.name }}</h3> 552 <h4 style="margin-top: 0;">Summary</h4> 553 <div class="header"> 554 <div> 555 <div class="el-descriptions"> 556 <div class="el-descriptions__body"> 557 <table class="el-descriptions__table is-bordered"> 558 <tbody> 559 <tr> 560 <td class="el-descriptions__cell el-descriptions__label is-bordered-label">Test Start</td> 561 <td class="el-descriptions__cell el-descriptions__content is-bordered-content">{{ case.begin }}</td> 562 <td class="el-descriptions__cell el-descriptions__label is-bordered-label">Test End</td> 563 <td class="el-descriptions__cell el-descriptions__content is-bordered-content">{{ case.end }}</td> 564 <td class="el-descriptions__cell el-descriptions__label is-bordered-label">Elapsed</td> 565 <td class="el-descriptions__cell el-descriptions__content is-bordered-content">{{ case.elapsed }}</td> 566 </tr> 567 {% if case.subcases %} 568 569 <!-- 测试套,无Error/Result,不显示 --> 570 {% if case.error %} 571 <tr> 572 <td class="el-descriptions__cell el-descriptions__label is-bordered-label">Logs</td> 573 <td class="el-descriptions__cell el-descriptions__content is-bordered-content" colspan="5">{{ case.logs }}</td> 574 </tr> 575 {% endif %} 576 577 {% else %} 578 <!-- 测试用例 --> 579 <tr> 580 <td class="el-descriptions__cell el-descriptions__label is-bordered-label">Result</td> 581 <td class="el-descriptions__cell el-descriptions__content is-bordered-content {{ case.result | lower }}">{{ case.result }}</td> 582 <td class="el-descriptions__cell el-descriptions__label is-bordered-label">Logs</td> 583 <td class="el-descriptions__cell el-descriptions__content is-bordered-content" colspan="3">{{ case.logs }}</td> 584 </tr> 585 {% if case.error %} 586 <tr> 587 <td class="el-descriptions__cell el-descriptions__label is-bordered-label">Error</td> 588 <td class="el-descriptions__cell el-descriptions__content is-bordered-content col_error error" colspan="5">{{ case.error }}</td> 589 </tr> 590 {% endif %} 591 592 {% endif %} 593 </tbody> 594 </table> 595 </div> 596 </div> 597 </div> 598 </div> 599 600 {% if case.subcases %} 601 <div class="el-table--fit el-table--enable-row-hover el-table--enable-row-transition el-table table-margin" 602 data-prefix="el" style="width: 100%;"> 603 <div class="el-table__inner-wrapper"> 604 <div class="el-table__header-wrapper"> 605 <table class="el-table__header" cellpadding="0" cellspacing="0" style="width: 100%; border: 0;"> 606 <colgroup> 607 <col name="el-table_1_column_1" style="width: 3%;"> 608 <col name="el-table_1_column_2" style="width: 30%;"> 609 <col name="el-table_1_column_3" style="width: 7%;"> 610 <col name="el-table_1_column_4" style="width: 42%;"> 611 <col name="el-table_1_column_5" style="width: 9%;"> 612 <col name="el-table_1_column_6" style="width: 9%;"> 613 </colgroup> 614 <thead class=""> 615 <tr class=""> 616 <th class="el-table_1_column_1 is-leaf el-table__cell"> 617 <div class="cell">#</div> 618 </th> 619 <th class="el-table_1_column_2 is-leaf el-table__cell"> 620 <div class="cell">Testcase</div> 621 </th> 622 <th class="el-table_1_column_3 is-leaf el-table__cell"> 623 <div class="cell">Result</div> 624 </th> 625 <th class="el-table_1_column_4 is-leaf el-table__cell"> 626 <div class="cell">Error</div> 627 </th> 628 <th class="el-table_1_column_5 is-leaf el-table__cell"> 629 <div class="cell">Begin</div> 630 </th> 631 <th class="el-table_1_column_6 is-leaf el-table__cell"> 632 <div class="cell">Elapsed</div> 633 </th> 634 </tr> 635 </thead> 636 </table> 637 </div> 638 <div class="el-table__body-wrapper"> 639 <div class="el-scrollbar"> 640 <div class="el-scrollbar__wrap el-scrollbar__wrap--hidden-default"> 641 <div class="el-scrollbar__view" style="display: inline-block; vertical-align: middle;"> 642 <table class="el-table__body" cellspacing="0" cellpadding="0" 643 style="table-layout: fixed; width: 100%; border: 0;"> 644 <colgroup> 645 <col name="el-table_1_column_1" style="width: 3%;"> 646 <col name="el-table_1_column_2" style="width: 30%;"> 647 <col name="el-table_1_column_3" style="width: 7%;"> 648 <col name="el-table_1_column_4" style="width: 42%;"> 649 <col name="el-table_1_column_5" style="width: 9%;"> 650 <col name="el-table_1_column_6" style="width: 9%;"> 651 </colgroup> 652 <tbody id="casesTableBody"> 653 {% for subcase in case.subcases %} 654 <tr class="el-table__row"> 655 <td class="el-table_1_column_1 el-table__cell"> 656 <div class="cell"><span>{{ loop.index }}</span></div> 657 </td> 658 <td class="el-table_1_column_2 el-table__cell"> 659 <div class="cell el-tooltip"><a href="{{ subcase.report }}" target="_blank">{{ subcase.name }}</a> 660 </div> 661 </td> 662 <td class="el-table_1_column_3 el-table__cell"> 663 <div class="cell {{ subcase.result | lower }}"><span>{{ subcase.result }}</span></div> 664 </td> 665 <td class="el-table_1_column_4 el-table__cell"> 666 <div class="cell error"><span>{{ subcase.error }}</span></div> 667 </td> 668 <td class="el-table_1_column_5 el-table__cell"> 669 <div class="cell"><span>{{ subcase.begin }}</span></div> 670 </td> 671 <td class="el-table_1_column_6 el-table__cell"> 672 <div class="cell"><span>{{ subcase.elapsed }}</span></div> 673 </td> 674 </tr> 675 {% endfor %} 676 </tbody> 677 </table> 678 </div> 679 </div> 680 </div> 681 </div> 682 </div> 683 </div> 684 {% endif %} 685 686 {% if case.devices %} 687 <h4>Test Devices</h4> 688 <div class="el-table--fit fit-height el-table--enable-row-hover el-table--enable-row-transition el-table" 689 data-prefix="el" style="width: 100%; max-height: 120px;"> 690 <div class="el-table__inner-wrapper wrapper-height" style="max-height: 120px;"> 691 <div class="el-table__header-wrapper"> 692 <table class="el-table__header" cellpadding="0" cellspacing="0" style="width: 100%; border: 0;"> 693 <colgroup> 694 <col name="el-table_1_column_1" style="width: 3%;"> 695 <col name="el-table_1_column_2" style="width: 22%;"> 696 <col name="el-table_1_column_3" style="width: 14%;"> 697 <col name="el-table_1_column_4" style="width: 14%;"> 698 <col name="el-table_1_column_5" style="width: 14%;"> 699 <col name="el-table_1_column_6" style="width: 16%;"> 700 <col name="el-table_1_column_7" style="width: 17%;"> 701 </colgroup> 702 <thead class=""> 703 <tr class=""> 704 <th class="el-table_1_column_1 is-leaf el-table__cell"> 705 <div class="cell">#</div> 706 </th> 707 <th class="el-table_1_column_2 is-leaf el-table__cell"> 708 <div class="cell">SN</div> 709 </th> 710 <th class="el-table_1_column_3 is-leaf el-table__cell"> 711 <div class="cell">Model</div> 712 </th> 713 <th class="el-table_1_column_4 is-leaf el-table__cell"> 714 <div class="cell">Type</div> 715 </th> 716 <th class="el-table_1_column_5 is-leaf el-table__cell"> 717 <div class="cell">Platform</div> 718 </th> 719 <th class="el-table_1_column_6 is-leaf el-table__cell"> 720 <div class="cell">Version</div> 721 </th> 722 <th class="el-table_1_column_7 is-leaf el-table__cell"> 723 <div class="cell">Others</div> 724 </th> 725 </tr> 726 </thead> 727 </table> 728 </div> 729 <div class="el-table__body-wrapper"> 730 <div class="el-scrollbar"> 731 <div class="el-scrollbar__wrap" style="max-height: calc(80px);"> 732 <div class="el-scrollbar__view" style="display: inline-block; vertical-align: middle;"> 733 <table class="el-table__body" cellspacing="0" cellpadding="0" 734 style="table-layout: fixed; width: 100%; border: 0;"> 735 <colgroup> 736 <col name="el-table_1_column_1" style="width: 3%;"> 737 <col name="el-table_1_column_2" style="width: 22%;"> 738 <col name="el-table_1_column_3" style="width: 14%;"> 739 <col name="el-table_1_column_4" style="width: 14%;"> 740 <col name="el-table_1_column_5" style="width: 14%;"> 741 <col name="el-table_1_column_6" style="width: 16%;"> 742 <col name="el-table_1_column_7" style="width: 17%;"> 743 </colgroup> 744 <tbody> 745 {% for device in case.devices %} 746 <tr class="el-table__row"> 747 <td class="el-table_1_column_1 el-table__cell"> 748 <div class="cell"><span>{{ loop.index }}</span></div> 749 </td> 750 <td class="el-table_1_column_2 el-table__cell"> 751 <div class="cell el-tooltip"><span>{{ device.sn }}</span></div> 752 </td> 753 <td class="el-table_1_column_3 el-table__cell"> 754 <div class="cell"><span>{{ device.model }}</span></div> 755 </td> 756 <td class="el-table_1_column_4 el-table__cell"> 757 <div class="cell"><span>{{ device.type }}</span></div> 758 </td> 759 <td class="el-table_1_column_5 el-table__cell"> 760 <div class="cell"><span>{{ device.platform }}</span></div> 761 </td> 762 <td class="el-table_1_column_6 el-table__cell"> 763 <div class="cell"><span>{{ device.version }}</span></div> 764 </td> 765 {% if device.others %} 766 <td class="el-table_1_column_7 el-table__cell"> 767 <div class="cell"><span>{{ device.others }}</span></div> 768 </td> 769 {% else %} 770 <td class="el-table_1_column_7 el-table__cell"> 771 <div class="cell"><span>-</span></div> 772 </td> 773 {% endif %} 774 </tr> 775 {% endfor %} 776 </tbody> 777 </table> 778 </div> 779 </div> 780 </div> 781 </div> 782 </div> 783 </div> 784 {% endif %} 785 786 <div id="details"> 787 <h4>Test Details</h4> 788 <div class="toolbar"> 789 <ul id="phoneErrorPosition" ref="phoneLeft"> 790 </ul> 791 <div class="err" style="overflow: auto; margin-left: 10px; width: 75%; position: relative; min-height: 625px;"> 792 <div class="el-descriptions__body err-scroll" style="margin-top: 10px;"> 793 <table class="el-descriptions__table is-bordered"> 794 <tbody id="message"> 795 </tbody> 796 </table> 797 </div> 798 <div class="img-div"> 799 </div> 800 </div> 801 </div> 802 </div> 803 804 <div id="dialog" class="mask"> 805 <div class="mask-imgBg"></div> 806 <div class="mask-delete" onclick="closeDialog()"> 807 <svg style="vertical-align: -0.125em" focusable="false" data-icon="close" 808 width="1em" height="1em" fill="currentColor" aria-hidden="true" fill-rule="evenodd" viewBox="64 64 896 896"> 809 <path 810 d="M799.86 166.31c.02 0 .04.02.08.06l57.69 57.7c.04.03.05.05.06.08a.12.12 0 010 .06c0 .03-.02.05-.06.09L569.93 512l287.7 287.7c.04.04.05.06.06.09a.12.12 0 010 .07c0 .02-.02.04-.06.08l-57.7 57.69c-.03.04-.05.05-.07.06a.12.12 0 01-.07 0c-.03 0-.05-.02-.09-.06L512 569.93l-287.7 287.7c-.04.04-.06.05-.09.06a.12.12 0 01-.07 0c-.02 0-.04-.02-.08-.06l-57.69-57.7c-.04-.03-.05-.05-.06-.07a.12.12 0 010-.07c0-.03.02-.05.06-.09L454.07 512l-287.7-287.7c-.04-.04-.05-.06-.06-.09a.12.12 0 010-.07c0-.02.02-.04.06-.08l57.7-57.69c.03-.04.05-.05.07-.06a.12.12 0 01.07 0c.03 0 .05.02.09.06L512 454.07l287.7-287.7c.04-.04.06-.05.09-.06a.12.12 0 01.07 0z"> 811 </path> 812 </svg> 813 </div> 814 </div> 815 </div> 816 817 <div class="btns" 818 style="position: fixed; z-index: 5000; bottom: 90px; right: 90px; display: none; border: 1px solid #d1cdcd;"> 819 <div class="btn-div"> 820 <div id="svg-show-media" style="display: none;" title="显示图片/视频"> 821 <svg onclick="displayMedia('block')" class="svg-style" xmlns="http://www.w3.org/2000/svg" 822 viewBox="0 0 1024 1024" data-v-ea893728=""> 823 <path fill="currentColor" 824 d="M512 160c320 0 512 352 512 352S832 864 512 864 0 512 0 512s192-352 512-352m0 64c-225.28 0-384.128 208.064-436.8 288 52.608 79.872 211.456 288 436.8 288 225.28 0 384.128-208.064 436.8-288-52.608-79.872-211.456-288-436.8-288zm0 64a224 224 0 1 1 0 448 224 224 0 0 1 0-448m0 64a160.192 160.192 0 0 0-160 160c0 88.192 71.744 160 160 160s160-71.808 160-160-71.744-160-160-160"> 825 </path> 826 </svg> 827 </div> 828 <div id="svg-hide-media" title="隐藏图片/视频"> 829 <svg onclick="displayMedia('none')" class="svg-style" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" 830 data-v-ea893728=""> 831 <path fill="currentColor" 832 d="M876.8 156.8c0-9.6-3.2-16-9.6-22.4-6.4-6.4-12.8-9.6-22.4-9.6-9.6 0-16 3.2-22.4 9.6L736 220.8c-64-32-137.6-51.2-224-60.8-160 16-288 73.6-377.6 176C44.8 438.4 0 496 0 512s48 73.6 134.4 176c22.4 25.6 44.8 48 73.6 67.2l-86.4 89.6c-6.4 6.4-9.6 12.8-9.6 22.4 0 9.6 3.2 16 9.6 22.4 6.4 6.4 12.8 9.6 22.4 9.6 9.6 0 16-3.2 22.4-9.6l704-710.4c3.2-6.4 6.4-12.8 6.4-22.4Zm-646.4 528c-76.8-70.4-128-128-153.6-172.8 28.8-48 80-105.6 153.6-172.8C304 272 400 230.4 512 224c64 3.2 124.8 19.2 176 44.8l-54.4 54.4C598.4 300.8 560 288 512 288c-64 0-115.2 22.4-160 64s-64 96-64 160c0 48 12.8 89.6 35.2 124.8L256 707.2c-9.6-6.4-19.2-16-25.6-22.4Zm140.8-96c-12.8-22.4-19.2-48-19.2-76.8 0-44.8 16-83.2 48-112 32-28.8 67.2-48 112-48 28.8 0 54.4 6.4 73.6 19.2zM889.599 336c-12.8-16-28.8-28.8-41.6-41.6l-48 48c73.6 67.2 124.8 124.8 150.4 169.6-28.8 48-80 105.6-153.6 172.8-73.6 67.2-172.8 108.8-284.8 115.2-51.2-3.2-99.2-12.8-140.8-28.8l-48 48c57.6 22.4 118.4 38.4 188.8 44.8 160-16 288-73.6 377.6-176C979.199 585.6 1024 528 1024 512s-48.001-73.6-134.401-176Z"> 833 </path> 834 <path fill="currentColor" 835 d="M511.998 672c-12.8 0-25.6-3.2-38.4-6.4l-51.2 51.2c28.8 12.8 57.6 19.2 89.6 19.2 64 0 115.2-22.4 160-64 41.6-41.6 64-96 64-160 0-32-6.4-64-19.2-89.6l-51.2 51.2c3.2 12.8 6.4 25.6 6.4 38.4 0 44.8-16 83.2-48 112-32 28.8-67.2 48-112 48Z"> 836 </path> 837 </svg> 838 </div> 839 </div> 840 <div title="查看错误详情" class="btn-div"> 841 <svg onclick="gotoTrace()" class="svg-style" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" 842 data-v-ea893728=""> 843 <path fill="currentColor" 844 d="M832 384H576V128H192v768h640zm-26.496-64L640 154.496V320zM160 64h480l256 256v608a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V96a32 32 0 0 1 32-32m160 448h384v64H320zm0-192h160v64H320zm0 384h384v64H320z"> 845 </path> 846 </svg> 847 </div> 848 <div title="回到顶部" class="scroll-top-btn"><svg class="svg-style" xmlns="http://www.w3.org/2000/svg" 849 viewBox="0 0 1024 1024" data-v-ea893728=""> 850 <path fill="currentColor" 851 d="M572.235 205.282v600.365a30.118 30.118 0 1 1-60.235 0V205.282L292.382 438.633a28.913 28.913 0 0 1-42.646 0 33.43 33.43 0 0 1 0-45.236l271.058-288.045a28.913 28.913 0 0 1 42.647 0L834.5 393.397a33.43 33.43 0 0 1 0 45.176 28.913 28.913 0 0 1-42.647 0l-219.618-233.23z"> 852 </path> 853 </svg></div> 854 </div> 855 856 <h4 class="h4">Execution Log</h4> 857 <div id="logs"> 858 {% for log in logs.content %} 859 <div>{{ log | replace("\n", "<br>") }}</div> 860 {% endfor %} 861 </div> 862 863 <script> 864 const reportData = {{ case }}; 865 866 const element = document.querySelector('#logs'); 867 const elementPosition = element.offsetTop; 868 const btns = document.querySelector('.btns'); 869 const scrollButton = document.querySelector('.scroll-top-btn'); 870 window.addEventListener('scroll', () => { 871 if (window.scrollY > elementPosition + 200) { 872 btns.style.display = 'block'; 873 } else { 874 btns.style.display = 'none'; 875 } 876 }); 877 scrollButton.addEventListener('click', function () { 878 window.scrollTo({ 879 top: 0, 880 behavior: 'smooth' 881 }); 882 }); 883 884 // 获取ul元素 885 const ul = document.getElementById('phoneErrorPosition'); 886 const imgDiv = document.querySelector('.img-div'); 887 const err = document.querySelector('.err'); 888 const dialog = document.getElementById('dialog'); 889 const maskImgBg = document.querySelector('.mask-imgBg'); 890 const detailsId = document.getElementById('details'); 891 892 const { steps } = reportData; 893 if (!steps?.length) { 894 detailsId.remove(); 895 } else { 896 addLi(); 897 } 898 899 // 定义一个函数,用于将li数据渲染成HTML字符串 900 function renderLi(data, index) { 901 return `<li class="${data.error !== '' ? 'isPassed' : ''} ${data.isChecked ? 'isChecked' : ''}" onclick="clickLi(${index})"> 902 <div class="li-div"> 903 <span title="${data.name}" class="li-span">${data.name}</span> 904 <span> 905 <span class="li-tag">${data.cost}s</span> 906 <span style="vertical-align: middle;" title="查看日志" onclick="gotoStep(${index})"> 907 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" data-v-ea893728=""><path fill="currentColor" d="M832 384H576V128H192v768h640zm-26.496-64L640 154.496V320zM160 64h480l256 256v608a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V96a32 32 0 0 1 32-32m160 448h384v64H320zm0-192h160v64H320zm0 384h384v64H320z"></path></svg> 908 </span> 909 </span> 910 </div> 911 </li>`; 912 } 913 914 function renderTr(key, value) { 915 return `<td class="el-descriptions__cell el-descriptions__label is-bordered-label" colspan="1">${key}</td> 916 <td class="el-descriptions__cell el-descriptions__content is-bordered-content" colspan="3">${value}</td>`; 917 } 918 919 // 定义一个函数,用于添加li元素 920 function clickLi(index) { 921 const liList = ul.getElementsByTagName('li'); // 获取所有li元素 922 for (let i = 0; i < liList.length; i++) { 923 liList[i].classList.remove('isChecked'); // 移除class-name类名 924 } 925 const li = ul.getElementsByTagName('li')[index]; // 获取点击的li元素 926 li.classList.add('isChecked'); // 添加class-name 927 showStep(steps[index]); 928 } 929 930 function addLi() { 931 let step = steps.findIndex(item => item.error !== ''); 932 if (step !== -1) { 933 steps[step].isChecked = true; 934 showStep(steps[step]); 935 } else { 936 steps[0].isChecked = true; 937 showStep(steps[0]); 938 } 939 steps.forEach((item, index) => { 940 ul.innerHTML += renderLi(item, index); 941 }); 942 } 943 944 function showStep(step) { 945 const message = document.getElementById('message'); 946 while (message.firstChild) { 947 message.removeChild(message.firstChild); 948 } 949 if (step.error) { 950 message.innerHTML = `<td class="el-descriptions__cell el-descriptions__label is-bordered-label" colspan="1">Error</td> 951 <td class="errorInfo el-descriptions__cell el-descriptions__content is-bordered-content error-content" colspan="7" 952 style="color: red;">${step.error} 953 </td>` 954 } 955 let tds = [] 956 for (const key in step.extras) { 957 let h = renderTr(key, step.extras[key]); 958 tds.push(h); 959 } 960 let count = 0; 961 let trs = null; 962 // 遍历所有的td元素 963 for (let i = 0; i < tds.length; i++) { 964 // 如果计数器为0,则创建一个新的tr元素 965 if (count === 0) { 966 trs = document.createElement('tr'); 967 } 968 // 将当前的td元素添加到tr元素中 969 trs.innerHTML += tds[i]; 970 // 计数器加1 971 count++; 972 // 如果计数器为2,则将tr元素添加到表格中,并将计数器重置为0 973 if (count === 2) { 974 message.appendChild(trs); 975 count = 0; 976 } 977 } 978 // 如果计数器不为0,则将剩余的td元素添加到一个新的tr元素中,并将其添加到表格中 979 if (count !== 0) { 980 message.appendChild(trs); 981 } 982 const screen = step.screenshot.reduce((acc, cur) => { 983 const prefix = cur.link.split('.')[0]; // 获取字符串中第一个 . 之前的部分作为前缀 984 if (!acc[prefix]) { 985 acc[prefix] = []; // 如果前缀不存在,则创建一个新的数组 986 } 987 acc[prefix].push(cur); // 将当前字符串添加到对应的数组中 988 return acc; 989 }, {}); 990 const numOfProperties = Object.keys(screen).length; 991 const errScroll = document.querySelector('.err-scroll'); 992 imgDiv.scrollTop = 0; 993 errScroll.scrollTop = 0; 994 while (imgDiv.firstChild) { 995 imgDiv.removeChild(imgDiv.firstChild); 996 } 997 if (numOfProperties > 1) { 998 multipleSn(screen); 999 } else { 1000 singleSn(screen); 1001 } 1002 if (message.children.length === 0) { 1003 errScroll.style.marginTop = 0; 1004 } else { 1005 errScroll.style.marginTop = '10px'; 1006 } 1007 } 1008 1009 function singleSn(screen) { 1010 for (const [key, item] of Object.entries(screen)) { 1011 const title = document.createElement('div'); 1012 const screenDiv = document.createElement('div'); 1013 screenDiv.scrollLeft = 0; 1014 title.textContent = key.split('/')[1]; 1015 screenDiv.appendChild(title); 1016 screenDiv.className = 'screen-div'; 1017 screenDiv.style.textAlign = 'center'; 1018 title.style.backgroundColor = '#f5f7fa'; 1019 title.style.fontWeight = 700; 1020 title.style.lineHeight = '38px'; 1021 title.style.color = '#606266'; 1022 title.style.fontSize = '15px'; 1023 const div2 = document.createElement('div'); 1024 div2.style.display = 'flex'; 1025 imgDiv.appendChild(title); 1026 imgDiv.style.display = 'block'; 1027 imgDiv.style.textAlign = 'center'; 1028 imgDiv.style.overflowX = 'hidden'; 1029 screenDiv.style.overflowX = 'auto'; 1030 for (let i = 0; i < item.length; i++) { 1031 const div = document.createElement('div'); 1032 div.style.padding = '10px 0 20px 0'; 1033 if (item.length > 1) { 1034 div.style.marginRight = '50px'; 1035 } 1036 screenDiv.style.marginRight = '0'; 1037 const div3 = document.createElement('div'); 1038 if (item[i].link.includes('.avi') || item[i].link.includes('.mp4')) { 1039 div.appendChild(isVideo(item[i].link)); 1040 } else { 1041 const img = isImage(item[i].link); 1042 div.appendChild(img); 1043 img.onload = function() { 1044 div3.style.width = img.width + 'px'; 1045 } 1046 } 1047 const fileName = item[i].name; 1048 addStyle(div3, fileName); 1049 div.appendChild(div3); 1050 div2.appendChild(div); 1051 screenDiv.appendChild(div2); 1052 } 1053 imgDiv.appendChild(screenDiv); 1054 } 1055 } 1056 1057 function multipleSn(screen) { 1058 const noSingleDiv = document.createElement('div'); 1059 noSingleDiv.style.backgroundColor = '#f5f7fa'; 1060 noSingleDiv.style.height = '38px'; 1061 noSingleDiv.style.width = '100%'; 1062 noSingleDiv.style.position = 'absolute'; 1063 noSingleDiv.style.zIndex = -1; 1064 imgDiv.appendChild(noSingleDiv); 1065 const keys = Object.keys(screen); 1066 const maxLength = Math.max(...keys.map(key => screen[key].length)); 1067 // 构建新数组 1068 const result = []; 1069 for (let i = 0; i < maxLength; i++) { 1070 const item = {}; 1071 keys.forEach(key => { 1072 item[key] = screen[key][i] || { 1073 link: '', name: '' 1074 }; // 如果属性值数组长度不够,用空字符串填充 1075 }); 1076 result.push(item); 1077 } 1078 const tableImg = document.createElement('table'); 1079 const theadImg = document.createElement('thead'); 1080 const theadTrImg = document.createElement('tr'); 1081 keys.forEach(item => { 1082 const thImg = document.createElement('th'); 1083 thImg.textContent = item.split('/')[1]; 1084 thImg.style.position = 'sticky'; 1085 thImg.style.top = 0; 1086 thImg.style.backgroundColor = '#f5f7fa'; 1087 thImg.style.zIndex = 10; 1088 theadTrImg.appendChild(thImg); 1089 }) 1090 theadTrImg.style.height = '38px'; 1091 theadTrImg.style.color = '#606266'; 1092 theadTrImg.style.fontSize = '15px'; 1093 theadImg.appendChild(theadTrImg); 1094 tableImg.appendChild(theadImg); 1095 tableImg.style.borderCollapse = 'collapse'; 1096 const tbodyImg = document.createElement('tbody'); 1097 for (let i = 0; i < maxLength; i++) { 1098 const trImg = document.createElement('tr'); 1099 for (const key in result[i]) { 1100 const tdImg = document.createElement('td'); 1101 const div = document.createElement('div'); 1102 div.style.padding = '10px 0 20px 0'; 1103 const div3 = document.createElement('div'); 1104 div3.style.width = '230px'; 1105 if (result[i][key].link.includes('.avi') || result[i][key].link.includes('.mp4')) { 1106 div.appendChild(isVideo(result[i][key].link)); 1107 } else { 1108 const img = isImage(result[i][key].link); 1109 div.appendChild(img); 1110 img.onload = function() { 1111 div3.style.width = img.width + 'px'; 1112 }; 1113 } 1114 const fileName = result[i][key].name; 1115 addStyle(div3, fileName); 1116 div.appendChild(div3); 1117 div.style.minWidth = '470px'; 1118 tdImg.appendChild(div); 1119 trImg.appendChild(tdImg); 1120 } 1121 tbodyImg.appendChild(trImg); 1122 } 1123 tableImg.appendChild(tbodyImg); 1124 imgDiv.appendChild(tableImg); 1125 imgDiv.style.display = 'flex'; 1126 imgDiv.style.overflowX = 'auto'; 1127 imgDiv.style.textAlign = 'center'; 1128 } 1129 1130 function addStyle(div, fileName) { 1131 div.textContent = fileName; 1132 div.title = fileName; 1133 div.style.fontSize = '14px'; 1134 div.style.margin = '0 auto'; 1135 div.style.overflow = 'hidden'; 1136 div.style. whiteSpace = 'nowrap'; 1137 div.style.textOverflow = 'ellipsis'; 1138 } 1139 1140 function isVideo(item) { 1141 const video = document.createElement('video'); 1142 video.controls = 'controls'; 1143 video.muted = 'muted'; 1144 video.style.width = '236px'; 1145 video.style.height = '514px'; 1146 video.src = item; 1147 video.addEventListener('click', (event) => { 1148 clickScreenShot('video', item); 1149 if (!event.target.classList.contains('controls')) { 1150 event.preventDefault(); 1151 return false; 1152 } 1153 }); 1154 return video; 1155 } 1156 1157 function isImage(item) { 1158 const img = document.createElement('img'); 1159 img.src = item; 1160 img.alt = ''; 1161 img.style.cursor = 'pointer'; 1162 img.addEventListener('click', (event) => clickScreenShot('img', item)); 1163 if (item) { 1164 img.onerror = function () { 1165 img.style.width = '230px'; 1166 }; 1167 } 1168 return img; 1169 } 1170 1171 const clickScreenShot = (type, index) => { 1172 const screenHeight = window.innerHeight; 1173 dialog.style.display = 'flex'; 1174 if (type === 'video') { 1175 const video = document.createElement('video'); 1176 video.controls = 'controls'; 1177 video.muted = 'muted'; 1178 video.style.width = 'auto'; 1179 video.style.height = 0.8 * screenHeight + 'px'; 1180 video.src = index; 1181 maskImgBg.appendChild(video); 1182 } else { 1183 const img = document.createElement('img'); 1184 img.src = index; 1185 img.alt = ''; 1186 img.style.cursor = 'pointer'; 1187 img.style.width = 'auto'; 1188 img.style.height = 0.8 * screenHeight + 'px'; 1189 maskImgBg.appendChild(img); 1190 } 1191 } 1192 1193 function closeDialog() { 1194 const images = maskImgBg.getElementsByTagName('img'); 1195 for (let i = 0; i < images.length; i++) { 1196 maskImgBg.removeChild(images[i]); 1197 } 1198 const videos = maskImgBg.getElementsByTagName('video'); 1199 for (let i = 0; i < videos.length; i++) { 1200 maskImgBg.removeChild(videos[i]); 1201 } 1202 dialog.style.display = 'none'; 1203 } 1204 1205 function displayMedia(value) { 1206 const el_svg_show_media = document.getElementById('svg-show-media'); 1207 const el_svg_hide_media = document.getElementById('svg-hide-media'); 1208 const el_log = document.getElementById('logs'); 1209 const el_images = el_log.getElementsByTagName('img'); 1210 const el_videos = el_log.getElementsByTagName('video'); 1211 1212 let elements = []; 1213 elements.push(...el_images, ...el_videos); 1214 for (let i = 0; i < elements.length; i++) { 1215 let element = elements[i]; 1216 let element_a = element.parentNode; 1217 element.src = element_a.href; 1218 if (value === 'block') { 1219 element.style.display = 'block'; 1220 el_svg_hide_media.style.display = 'block'; 1221 el_svg_show_media.style.display = 'none'; 1222 } else { 1223 element.style.display = 'none'; 1224 el_svg_hide_media.style.display = 'none'; 1225 el_svg_show_media.style.display = 'block'; 1226 } 1227 } 1228 } 1229 1230 function gotoStep(stepId) { 1231 let element = document.getElementById(stepId); 1232 if (element != null) { 1233 let distance = element.offsetTop - 50; 1234 document.documentElement.scrollTo(0, distance); 1235 } 1236 } 1237 1238 function gotoTrace() { 1239 const columns = document.getElementsByClassName('col_error'); 1240 if (columns.length === 0) { 1241 return; 1242 } 1243 const errorMsg = columns[0].textContent; 1244 if (!errorMsg) { 1245 return; 1246 } 1247 const elements = document.getElementById('logs').getElementsByClassName('error'); 1248 for (let i = 0; i < elements.length; i++) { 1249 let element = elements[i]; 1250 if (element.textContent.indexOf(errorMsg) >= 0) { 1251 document.documentElement.scrollTop = element.offsetTop; 1252 break; 1253 } 1254 } 1255 } 1256 1257 const rainbowText = document.getElementById('rainbowText'); 1258 const textLength = rainbowText.offsetWidth; 1259 rainbowText.style.backgroundSize = `${textLength}px 100%`; 1260 </script> 1261</body> 1262 1263</html>