• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5    <meta charset="UTF-8">
6    <title>Report-Diff</title>
7    <style>
8        * {
9            box-sizing: border-box;
10        }
11
12        html,
13        body {
14            height: 100%;
15            margin: 0;
16            padding: 0;
17        }
18
19        lit-tabs {
20            padding-top: 10px;
21        }
22
23        lit-tabpane {
24            padding: 20px;
25        }
26    </style>
27</head>
28
29<body>
30    <script type="module">
31        window.getShadowRoot = (el) => {
32            if (el.parentNode) {
33                return window.getShadowRoot(el.parentNode)
34            } else {
35                return el;
36            }
37        };
38        window.getShadowElement = (el) => {
39            if (el.parentElement) {
40                return window.getShadowElement(el.parentElement)
41            } else {
42                return el;
43            }
44        };
45        class LitIcon extends HTMLElement {
46            static get observedAttributes() {
47                return ["name", "size", "color", "path"]
48            }
49
50            constructor() {
51                super();
52                const shadowRoot = this.attachShadow({ mode: 'open' });
53                shadowRoot.innerHTML = `
54        <style>
55        :host{
56            font-size: inherit;
57            display: inline-block;
58            transition: .3s;
59         }
60         :host([spin]){
61            animation: rotate 1.75s linear infinite;
62         }
63         @keyframes rotate {
64            to{
65                transform: rotate(360deg);
66            }
67         }
68         .icon{
69            display: block;
70            width: 1em;
71            height: 1em;
72            margin: auto;
73            fill: currentColor;
74            overflow: hidden;
75         }
76        </style>
77        <svg style="display: none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
78            <symbol id="icon-ellipsis" viewBox="0 0 1024 1024"><path d="M232 511m-56 0a56 56 0 1 0 112 0 56 56 0 1 0-112 0Z"></path><path d="M512 511m-56 0a56 56 0 1 0 112 0 56 56 0 1 0-112 0Z"></path><path d="M792 511m-56 0a56 56 0 1 0 112 0 56 56 0 1 0-112 0Z"></path></symbol>
79            <symbol id="icon-doubleleft" viewBox="0 0 1024 1024"><path d="M272.9 512l265.4-339.1c4.1-5.2 0.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L186.8 492.3c-9.1 11.6-9.1 27.9 0 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H532c6.7 0 10.4-7.7 6.3-12.9L272.9 512z"></path><path d="M576.9 512l265.4-339.1c4.1-5.2 0.4-12.9-6.3-12.9h-77.3c-4.9 0-9.6 2.3-12.6 6.1L490.8 492.3c-9.1 11.6-9.1 27.9 0 39.5l255.3 326.1c3 3.9 7.7 6.1 12.6 6.1H836c6.7 0 10.4-7.7 6.3-12.9L576.9 512z"></path></symbol>
80            <symbol id="icon-doubleright" viewBox="0 0 1024 1024"><path d="M533.2 492.3L277.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H188c-6.7 0-10.4 7.7-6.3 12.9L447.1 512 181.7 851.1c-4.1 5.2-0.4 12.9 6.3 12.9h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"></path><path d="M837.2 492.3L581.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H492c-6.7 0-10.4 7.7-6.3 12.9L751.1 512 485.7 851.1c-4.1 5.2-0.4 12.9 6.3 12.9h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"></path></symbol>
81            <symbol id="icon-close-circle-fill" viewBox="0 0 1024 1024"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64z m165.4 618.2l-66-0.3L512 563.4l-99.3 118.4-66.1 0.3c-4.4 0-8-3.5-8-8 0-1.9 0.7-3.7 1.9-5.2l130.1-155L340.5 359c-1.2-1.5-1.9-3.3-1.9-5.2 0-4.4 3.6-8 8-8l66.1 0.3L512 464.6l99.3-118.4 66-0.3c4.4 0 8 3.5 8 8 0 1.9-0.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></symbol>
82<!--            <symbol id="icon-left" viewBox="0 0 1024 1024"><path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8c-16.4 12.8-16.4 37.5 0 50.3l450.8 352.1c5.3 4.1 12.9 0.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path></symbol>-->
83<!--            <symbol id="icon-right" viewBox="0 0 1024 1024"><path d="M765.7 486.8L314.9 134.7c-5.3-4.1-12.9-0.4-12.9 6.3v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1c16.4-12.8 16.4-37.6 0-50.4z"></path></symbol>-->
84        </svg>
85        <svg class="icon" id="icon" aria-hidden="true" viewBox="0 0 ${this.view} ${this.view}">
86             ${this.path ? '<path id="path"></path>' : '<use id="use"></use>'}
87        </svg>
88        `
89            }
90
91            get view() {
92                return this.getAttribute("view") || 1024;
93            }
94
95            get name() {
96                return this.getAttribute("name");
97            }
98
99            get path() {
100                return this.getAttribute("path");
101            }
102
103            set name(value) {
104                this.setAttribute("name", value);
105            }
106
107            set path(value) {
108                this.setAttribute("path", value);
109            }
110
111            get size() {
112                return this.getAttribute("size") || "";
113            }
114
115            get color() {
116                return this.getAttribute("color") || "";
117            }
118
119            set size(value) {
120                this.setAttribute("size", value);
121            }
122
123            set color(value) {
124                this.setAttribute("color", value);
125            }
126
127            get spin() {
128                return this.hasAttribute('spin');
129            }
130
131            set spin(value) {
132                if (value) {
133                    this.setAttribute('spin', '')
134                } else {
135                    this.removeAttribute('spin');
136                }
137            }
138
139            //当 custom element首次被插入文档DOM时,被调用。
140            connectedCallback() {
141                this.icon = this.shadowRoot.getElementById("icon");
142                this.use = this.shadowRoot.querySelector("use")
143                this.d = this.shadowRoot.querySelector("path");
144                this.size && (this.size = this.size);
145                this.color && (this.color = this.color);
146                this.name && (this.name = this.name);
147                this.path && (this.path = this.path);
148            }
149
150            //当 custom element从文档DOM中删除时,被调用。
151            disconnectedCallback() {
152
153            }
154
155            //当 custom element被移动到新的文档时,被调用。
156            adoptedCallback() {
157                console.log('Custom square element moved to new page.');
158            }
159
160            //当 custom element增加、删除、修改自身属性时,被调用。
161            attributeChangedCallback(name, oldValue, newValue) {
162                if (name === "name" && this.use) {
163                    this.use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', `#icon-${newValue}`);
164                }
165                if (name === "path" && this.d) {
166                    this.d.setAttribute("d", newValue);
167                }
168                if (name === "color" && this.icon) {
169                    this.icon.style.color = newValue;
170                }
171                if (name === "size" && this.icon) {
172                    this.icon.style.fontSize = newValue + "px";
173                }
174            }
175        }
176        if (!customElements.get('lit-icon')) {
177            customElements.define('lit-icon', LitIcon);
178        }
179
180        class LitInput extends HTMLElement {
181            static get observedAttributes() {
182                return [
183                    'placeholder',//显示提示文字
184                    'block',//属性将使按钮适合其父宽度。
185                    'icon',//图标,如果配置了 slot='prefix' icon将失效
186                    'bordered',//是否有边框
187                    'allow-clear',//是否有清除
188                    'rows',//表示行数,默认input 设置rows 后 显示为textarea
189                    'maxlength',//允许输入多少个字符 默认没有限制
190                    'type',//password  number  text
191                    'pattern',//正则表达式
192                    'error-text',//错误提示文字
193                    'error-placement',//错误提示文字方向
194                    'required',//是否开始验证
195                    'value',//值
196                ];
197            }
198            get value() {
199                return this.getAttribute('value') || '';
200            }
201
202            set value(value) {
203                this.setAttribute('value', value);
204            }
205            get required() {
206                return this.hasAttribute('required');
207            }
208
209            set required(value) {
210                if (value) {
211                    this.setAttribute('required', "");
212                } else {
213                    this.removeAttribute('required');
214                }
215            }
216            get pattern() {
217                return this.getAttribute('pattern') || '';
218            }
219
220            set pattern(value) {
221                this.setAttribute('pattern', value);
222            }
223            get errorText() {
224                return this.getAttribute('error-text') || '该项为必填项';
225            }
226            set errorText(value) {
227                this.setAttribute('error-text', value)
228            }
229            get errorPlacement() {
230                return this.getAttribute('error-placement') || 'top';
231            }
232            set errorPlacement(value) {
233                this.setAttribute('error-placement', value)
234            }
235            get type() {
236                return this.getAttribute('type');
237            }
238            set type(value) {
239                this.setAttribute('type', value);
240            }
241            get maxlength() {
242                return this.getAttribute('maxlength') || '';
243            }
244
245            set maxlength(value) {
246                this.setAttribute('maxlength', value)
247            }
248            get rows() {
249                return this.getAttribute('rows');
250            }
251            set rows(value) {
252                this.setAttribute('rows', value);
253            }
254            get allowClear() {
255                return this.hasAttribute('allow-clear');
256            }
257
258            set allowClear(value) {
259                if (value) {
260                    this.setAttribute('allow-clear', '');
261                } else {
262                    this.removeAttribute('allow-clear');
263                }
264            }
265
266            get bordered() {
267                return this.getAttribute('bordered') || "true";
268            }
269
270            set bordered(value) {
271                if (value) {
272                    this.setAttribute('bordered', 'true');
273                } else {
274                    this.setAttribute('bordered', 'false')
275                }
276            }
277
278            get icon() {
279                return this.getAttribute('icon') || null;
280            }
281
282            set icon(value) {
283                this.setAttribute('icon', value)
284            }
285
286            get block() {
287                return this.hasAttribute('block')
288            }
289
290            set block(value) {
291                if (value) {
292                    this.setAttribute('block', '');
293                } else {
294                    this.removeAttribute('block')
295                }
296            }
297
298            get placeholder() {
299                return this.getAttribute('placeholder') || '';
300            }
301
302            set placeholder(value) {
303                this.setAttribute('placeholder', value);
304            }
305
306            constructor() {
307                super();
308                const shadowRoot = this.attachShadow({ mode: 'open' });
309                shadowRoot.innerHTML = `
310        <style>
311:host{
312    ${this.bordered === 'true' ? 'border: 1px solid #e9e9e9;' : ''}
313    display: inline-flex;
314    box-sizing: border-box;
315    position:relative;
316    align-items: ${this.rows ? 'flex-start' : 'center'};
317    transition: all .3s;
318    border-radius: 2px;
319    font-size: 14px;
320    color: #333;
321    ${this.block ? 'display: flex;' : ''}
322    box-sizing: border-box;
323 }
324 :host(:hover){
325    ${this.bordered === 'true' ? 'border: 1px solid #65b687;' : ''}
326    ${this.bordered === 'true' ? 'box-shadow: 0 0 10px #65b68766;' : ''}
327 }
328 *{
329    box-sizing: border-box;
330 }
331 .input{
332    outline: none;
333    border: 0px;
334    font-size: inherit;
335    color: inherit;
336    width: 100%;
337    height: 100%;
338    vertical-align:middle;
339    line-height:inherit;
340    height:inherit;
341    padding: 6px 6px 6px ${this.icon ? '6px' : '11px'};
342    max-height: inherit;
343    box-sizing: border-box;
344 }
345 /*去掉type=number 后面的增减箭头*/
346input::-webkit-outer-spin-button,
347input::-webkit-inner-spin-button {
348    -webkit-appearance: none;
349
350}
351
352input[type='number'] {
353    -moz-appearance: textfield;
354}
355
356 lit-tips{
357    flex: 1;
358    height: 100%;
359    width: 100%;
360 }
361 lit-tips{
362    flex: 1;
363    align-items: center;
364 }
365:host(:not([allow-clear])) .clear-btn{
366    display: flex;
367    visibility: hidden;
368    transition: all .3s;
369    opacity: 0;
370}
371:host([allow-clear]) .clear-btn{
372    opacity: 0;
373    position:absolute;
374    right: 0;
375    top: .5rem;
376    visibility: hidden;
377    transition: all .3s;
378    display: flex;
379    margin-right: 6px;
380    transform: translateY(0%);
381    color: #bfbfbf;
382}
383:host([allow-clear]) .clear-btn:hover{
384    color: #8c8c8c;
385}
386
387.area{
388    outline: none;
389    border: 0px;
390    width: 100%;
391    font-size: inherit;
392    vertical-align:middle;
393    min-height: calc(2rem);
394    max-height: calc(${this.rows} * 1rem);
395    padding: 6px 6px 6px ${this.icon ? '6px' : '11px'};
396    box-sizing: border-box;
397    resize:vertical;
398}
399
400:host(:not([type=password])) .pwd-btn,
401:host(:not([type=tel])) .pwd-btn{
402    display: none;
403    visibility: hidden;
404    transition: all .3s;
405    opacity: 0;
406}
407:host([type=password]) .pwd-btn,
408:host([type=tel]) .pwd-btn{
409    opacity: 1;
410    position:absolute;
411    right: 0;
412    top: .5rem;
413    visibility: visible;
414    transition: all .3s;
415    display: flex;
416    margin-right: 6px;
417    transform: translateY(0%);
418    color: #bfbfbf;
419}
420/*:host([type=password]) input{*/
421/*    -webkit-text-security:none;*/
422/*    text-security:none;*/
423/*}*/
424:host([type=password]) .pwd-btn:hover,
425:host([type=tel]) .pwd-btn:hover{
426    color: #8c8c8c;
427}
428
429</style>
430<slot name="prefix">${this.icon ? `<lit-icon name="${this.icon}" style="margin-left: 11px;color: inherit;font-size: inherit;${this.rows ? 'transform: translateY(50%);' : ''}"></lit-icon>` : ``}</slot>
431<lit-tips tips="${this.errorText}" placement="${this.errorPlacement}" color="#f4615c" offset="12px" show="false">
432    ${this.hasAttribute('rows') ?
433                        `<textarea class="area" rows="${this.rows}" value="${this.value}" placeholder="${this.placeholder}" cols="27" maxlength="${this.maxlength}" onscroll="this.rows++;"></textarea>`
434                        :
435                        `<input type="${this.type}" value="${this.value}" class="input" placeholder="${this.placeholder}"  >`}
436
437    <lit-icon class="clear-btn" name="close-circle-fill"></lit-icon>
438    <lit-icon class="pwd-btn" name="eye-fill"></lit-icon>
439</lit-tips>
440<slot name="suffix" ></slot>
441        `
442            }
443
444            //当 custom element首次被插入文档DOM时,被调用。
445            connectedCallback() {
446                this.reg = new RegExp(this.pattern);
447                this.inputElement = this.shadowRoot.querySelector('.input');
448                this.areaElement = this.shadowRoot.querySelector('.area');
449                this.clearElement = this.shadowRoot.querySelector('.clear-btn');
450                this.pwdElement = this.shadowRoot.querySelector('.pwd-btn');
451
452                this.pwdElement.onclick = (e) => {
453                    if (this.inputElement.getAttribute('type') === 'password') {
454                        this.pwdElement.name = 'eyeclose-fill';
455                        this.inputElement.setAttribute('type', 'tel');
456                    } else if (this.inputElement.getAttribute('type') === 'tel') {
457                        this.inputElement.setAttribute('type', 'password');
458                        this.pwdElement.name = 'eye-fill';
459                    }
460                }
461
462                if (this.areaElement) {
463                    this.clearElement.onclick = e => {
464                        this.areaElement.value = '';
465                        this.clearElement.style.visibility = 'hidden';
466                        this.clearElement.style.opacity = '0';
467                        this.value = this.areaElement.value;
468                        this.dispatchEvent(new CustomEvent('onClear', e));
469                        this.clearError();
470                    }
471                    this.areaElement.oninput = e => {
472                        if (this.allowClear) {
473                            if (this.areaElement.value.length > 0) {
474                                this.clearElement.style.visibility = 'visible';
475                                this.clearElement.style.opacity = '1';
476                            } else {
477                                this.clearElement.style.visibility = 'hidden';
478                                this.clearElement.style.opacity = '0';
479                            }
480                        }
481                        this.value = this.areaElement.value;
482                        this.verify();
483                        this.dispatchEvent(new CustomEvent('input', e));
484                    }
485                    this.areaElement.onchange = e => {
486                        this.value = this.areaElement.value;
487                        this.verify();
488                        this.dispatchEvent(new CustomEvent('change', e));
489                    }
490                    this.areaElement.onfocus = e => {
491                        this.value = this.areaElement.value;
492                        this.verify();
493                        this.dispatchEvent(new CustomEvent('focus', e));
494                    }
495                    this.areaElement.onblur = e => {
496                        this.value = this.areaElement.value;
497                        this.dispatchEvent(new CustomEvent('blur', e));
498                    }
499                    this.areaElement.onkeydown = e => {
500                        if (e.key === 'Enter') {
501                            this.value = this.areaElement.value;
502                            this.verify();
503                            this.dispatchEvent(new CustomEvent('onPressEnter', e));
504                        }
505                        this.dispatchEvent(new CustomEvent('keydown', e));
506                    }
507                    this.areaElement.onkeyup = e => {
508                        this.dispatchEvent(new CustomEvent('keyup', e));
509                    }
510                    this.areaElement.onselect = e => {
511                        this.dispatchEvent(new CustomEvent('select', e));
512                    }
513                    this.areaElement.onclick = e => {
514                        this.dispatchEvent(new CustomEvent('click', e));
515                    }
516                }
517                if (this.inputElement) {
518                    this.clearElement.onclick = e => {
519                        this.inputElement.value = '';
520                        this.clearElement.style.visibility = 'hidden';
521                        this.clearElement.style.opacity = '0';
522                        this.value = this.inputElement.value;
523                        this.dispatchEvent(new CustomEvent('onClear', e));
524                        this.clearError();
525                    }
526                    this.inputElement.oninput = e => {
527                        if (this.allowClear) {
528                            if (this.inputElement.value.length > 0) {
529                                this.clearElement.style.visibility = 'visible';
530                                this.clearElement.style.opacity = '1';
531                            } else {
532                                this.clearElement.style.visibility = 'hidden';
533                                this.clearElement.style.opacity = '0';
534                            }
535                        }
536                        this.value = this.inputElement.value;
537                        this.verify();
538                        this.dispatchEvent(new CustomEvent('input', e));
539                    }
540                    this.inputElement.onchange = e => {
541                        this.value = this.inputElement.value;
542                        this.verify();
543                        this.dispatchEvent(new CustomEvent('change', e));
544                    }
545                    this.inputElement.onfocus = e => {
546                        this.value = this.inputElement.value;
547                        this.verify();
548                        this.dispatchEvent(new CustomEvent('focus', e));
549                    }
550                    this.inputElement.onblur = e => {
551                        this.dispatchEvent(new CustomEvent('blur', e));
552                    }
553                    this.inputElement.onkeydown = e => {
554                        if (e.key === 'Enter') {
555                            this.value = this.inputElement.value;
556                            this.verify();
557                            this.dispatchEvent(new CustomEvent('onPressEnter', e));
558                        }
559                        this.dispatchEvent(new CustomEvent('keydown', e));
560                    }
561                    this.inputElement.onkeyup = e => {
562                        this.dispatchEvent(new CustomEvent('keyup', e));
563                    }
564                    this.inputElement.onselect = e => {
565                        this.dispatchEvent(new CustomEvent('select', e));
566                    }
567                    this.inputElement.onclick = e => {
568                        this.dispatchEvent(new CustomEvent('click', e));
569                    }
570                }
571
572            }
573
574            //当 custom element从文档DOM中删除时,被调用。
575            disconnectedCallback() {
576
577            }
578
579            //当 custom element被移动到新的文档时,被调用。
580            adoptedCallback() {
581                console.log('Custom square element moved to new page.');
582            }
583
584            //当 custom element增加、删除、修改自身属性时,被调用。
585            attributeChangedCallback(name, oldValue, newValue) {
586                if (name === 'value') {
587                    // console.log(name,oldValue,newValue);
588                    // console.log(this.inputElement);
589                    if (this.inputElement) {
590                        this.inputElement.value = newValue
591                    } else if (this.areaElement) {
592                        this.areaElement.value = newValue;
593                    }
594                }
595            }
596            verify() {
597                if (this.required) {
598                    let result;
599                    if (this.hasAttribute('rows')) {
600                        result = this.reg.test(this.areaElement.value);
601                    } else {
602                        result = this.reg.test(this.inputElement.value);
603                    }
604                    if (result) {
605                        this.shadowRoot.querySelector('lit-tips').show = 'false'
606                    } else {
607                        this.shadowRoot.querySelector('lit-tips').show = 'true'
608                    }
609                    return result;
610                } else {
611                    return true;
612                }
613            }
614            clearError() {
615                this.shadowRoot.querySelector('lit-tips').show = 'false'
616            }
617        }
618        if (!customElements.get('lit-input')) {
619            customElements.define('lit-input', LitInput);
620        }
621
622        class LitLoading extends HTMLElement {
623            static get observedAttributes() { return ['color', 'size'] }
624
625            constructor() {
626                super();
627                const shadowRoot = this.attachShadow({ mode: 'open' });
628                shadowRoot.innerHTML = `
629        <style>
630        :host{
631            font-size:inherit;
632            display:inline-flex;
633            align-items: center;
634            justify-content:center;
635            color:var(--themeColor,#42b983);
636        }
637        .loading{
638            display: block;
639            width: 1em;
640            height: 1em;
641            margin: auto;
642            animation: rotate 1.4s linear infinite;
643        }
644        .circle {
645            stroke: currentColor;
646            animation:  progress 1.4s ease-in-out infinite;
647            stroke-dasharray: 80px, 200px;
648            stroke-dashoffset: 0px;
649            transition:.3s;
650        }
651        :host(:not(:empty)) .loading{
652            margin:.5em;
653        }
654        @keyframes rotate{
655            to{
656                transform: rotate(360deg);
657            }
658        }
659        @keyframes progress {
660            0% {
661              stroke-dasharray: 1px, 200px;
662              stroke-dashoffset: 0px;
663            }
664            50% {
665              stroke-dasharray: 100px, 200px;
666              stroke-dashoffset: -15px;
667            }
668            100% {
669              stroke-dasharray: 100px, 200px;
670              stroke-dashoffset: -125px;
671            }
672        }
673        </style>
674        <svg class="loading" id="loading" viewBox="22 22 44 44"><circle class="circle" cx="44" cy="44" r="20.2" fill="none" stroke-width="3.6"></circle></svg>
675        <slot></slot>
676        `
677            }
678
679            get size() {
680                return this.getAttribute('size') || '';
681            }
682
683            get color() {
684                return this.getAttribute('color') || '';
685            }
686
687            set size(value) {
688                this.setAttribute('size', value);
689            }
690
691            set color(value) {
692                this.setAttribute('color', value);
693            }
694
695            connectedCallback() {
696                this.loading = this.shadowRoot.getElementById('loading');
697                this.size && (this.size = this.size);
698                this.color && (this.color = this.color);
699            }
700
701            attributeChangedCallback(name, oldValue, newValue) {
702                if (name == 'color' && this.loading) {
703                    this.loading.style.color = newValue;
704                }
705                if (name == 'size' && this.loading) {
706                    this.loading.style.fontSize = newValue + 'px';
707                }
708            }
709        }
710        if (!customElements.get('lit-loading')) {
711            customElements.define('lit-loading', LitLoading);
712        }
713
714        class LitPagination extends HTMLElement {
715            static get observedAttributes() {
716                return [
717                    "current",//当前页码
718                    "total",//总条数
719                    "page-size",//每页条数
720                    "disabled",//禁用分页
721                    'show-size-changer',//显示每页条目数select
722                    'show-quick-jumper',//显示跳至多少页
723                    'page-size-options',//指定每页可以显示多少条 默认 [10,20,50,100]
724                    'simple',//简洁模式
725                ]
726            }
727            get pageSizeOptions() {
728                return this.getAttribute('page-size-options');
729            }
730            set pageSizeOptions(value) {
731                this.setAttribute('page-size-options', '');
732            }
733
734            get current() {
735                return this.getAttribute('current') || '1';
736            }
737
738            set current(value) {
739                this.setAttribute('current', value);
740            }
741
742            get total() {
743                return this.getAttribute('total') || '50';
744            }
745
746            set total(value) {
747                this.setAttribute('total', value);
748            }
749
750            get pageSize() {
751                return this.getAttribute('page-size') || '10';
752            }
753
754            set pageSize(value) {
755                this.setAttribute('page-size', value);
756            }
757
758            get disabled() {
759                return this.hasAttribute('disabled');
760            }
761
762            set disabled(value) {
763                if (value) {
764                    this.setAttribute('disabled', '');
765                } else {
766                    this.removeAttribute('disabled');
767                }
768            }
769
770            get showSizeChanger() {
771                return this.hasAttribute('show-size-changer')
772            }
773
774            set showSizeChanger(value) {
775                if (value) {
776                    this.setAttribute('show-size-changer', '');
777                } else {
778                    this.removeAttribute('show-size-changer');
779                }
780            }
781
782            get showQuickJumper() {
783                return this.hasAttribute('show-quick-jumper')
784            }
785
786            set showQuickJumper(value) {
787                if (value) {
788                    this.setAttribute('show-quick-jumper', '');
789                } else {
790                    this.removeAttribute('show-quick-jumper');
791                }
792            }
793
794            get simple() {
795                return this.hasAttribute('simple');
796            }
797
798            set simple(value) {
799                if (value) {
800                    this.setAttribute('simple', '');
801                } else {
802                    this.removeAttribute('simple');
803                }
804            }
805
806            constructor() {
807                super();
808                const shadowRoot = this.attachShadow({ mode: 'open' });
809                shadowRoot.innerHTML = `
810<style>
811:host{
812     display: flex;
813     width: max-content;
814}
815.root{
816    display: inline-flex;
817    /*grid-template-columns: repeat(auto-fill,35px);*/
818    /*column-gap: 8px;*/
819    /*row-gap: 8px;*/
820    user-select: none;
821}
822.item{
823    box-sizing: border-box;
824    color: #333;
825    transition: all .3s;
826    width: 35px;
827    height: 35px;
828    margin-left: 6px;
829    padding: 6px 10px;
830    border: 1px solid #ececec;
831    border-radius: 3px;
832    cursor: pointer;
833    display: inline-flex;
834    align-items: center;
835    justify-content: center;
836}
837.item-dir{
838    color: #333;
839    transition: all .3s;
840    padding: 5px 10px;
841    /*border: 1px solid #ececec;*/
842    /*border-radius: 3px;*/
843    cursor: pointer;
844    display: inline-flex;
845    align-items: center;
846    justify-content: center;
847}
848.item:not([disable]):hover{
849    color:#42b983;
850    border: 1px solid #42b983;
851}
852.item[selected]{
853    color:#42b983;
854    border: 1px solid #42b983;
855}
856.item[disable]{
857    /*background-color: #f5f5f5;*/
858    fill: #c7c7c7;
859    color: #c7c7c7;
860    cursor: not-allowed;
861}
862
863:host([show-quick-jumper]) .jump-root{
864    grid-column: span 4;
865    margin-left: 6px;
866    display: inline-flex;
867    align-items: center;
868}
869:host(:not([show-quick-jumper])) .jump-root{
870    display: none;
871}
872
873:host([show-size-changer]) .pages-change{
874    display: inline-flex;
875    grid-column: span 3;
876    width: 120px;
877    margin-left: 6px;
878    font-size: .9rem;
879}
880:host(:not([show-size-changer])) .pages-change{
881    display: none;
882}
883</style>
884<div style="display: grid;grid-template-columns: max-content 1fr;">
885    <div style="display: inline-flex;align-items: center" id="showTotal">
886        <slot id="st" name="showTotal"></slot>
887    </div>
888    <div class="root">
889<!--        <lit-icon class="item left-arrow" name="left" ></lit-icon>-->
890        <svg class="item left-arrow" viewBox="0 0 1024 1024" aria-hidden="true" >
891            <path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8c-16.4 12.8-16.4 37.5 0 50.3l450.8 352.1c5.3 4.1 12.9 0.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"></path>
892        </svg>
893        <div class="item first">1</div>
894        <lit-icon class="item-dir double-left" name="ellipsis" ></lit-icon>
895<!--        <svg class="item-dir double-left" viewBox="0 0 1024 1024" aria-hidden="true" style="width: 16px;width: 16px;">-->
896<!--            <path d="M232 511m-56 0a56 56 0 1 0 112 0 56 56 0 1 0-112 0Z"></path><path d="M512 511m-56 0a56 56 0 1 0 112 0 56 56 0 1 0-112 0Z"></path><path d="M792 511m-56 0a56 56 0 1 0 112 0 56 56 0 1 0-112 0Z"></path>-->
897<!--        </svg>-->
898        <div class="item one">2</div>
899        <div class="item two">3</div>
900        <div class="item three">4</div>
901        <div class="item four">5</div>
902        <div class="item five">6</div>
903        <lit-icon class="item-dir double-right" name="ellipsis" ></lit-icon>
904<!--        <svg class="item-dir double-right" viewBox="0 0 1024 1024" aria-hidden="true" style="width: 16px;width: 16px;">-->
905<!--            <path d="M232 511m-56 0a56 56 0 1 0 112 0 56 56 0 1 0-112 0Z"></path><path d="M512 511m-56 0a56 56 0 1 0 112 0 56 56 0 1 0-112 0Z"></path><path d="M792 511m-56 0a56 56 0 1 0 112 0 56 56 0 1 0-112 0Z"></path>-->
906<!--        </svg>-->
907        <div class="item last">1</div>
908<!--        <lit-icon class="item right-arrow" name="right"></lit-icon>-->
909        <svg class="item right-arrow" viewBox="0 0 1024 1024" aria-hidden="true" >
910            <path d="M765.7 486.8L314.9 134.7c-5.3-4.1-12.9-0.4-12.9 6.3v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1c16.4-12.8 16.4-37.6 0-50.4z"></path>
911        </svg>
912        <lit-select class="pages-change" default-value="10"></lit-select>
913        <div class="jump-root">
914            <label style="font-size: .9rem;color: #333;margin-right: 5px">跳至</label>
915            <lit-input type="number" class="jump" style="width: 80px;height: 100%"></lit-input>
916            <label style="font-size: .9rem;color: #333;margin-left: 5px">页</label>
917        </div>
918    </div>
919
920</div>
921
922        `
923            }
924
925            //当 custom element首次被插入文档DOM时,被调用。
926            connectedCallback() {
927                this.slotShowTotal = null;
928                this.rootElement = this.shadowRoot.querySelector('.root');
929                let st = this.shadowRoot.querySelector('#st');
930                st.addEventListener('slotchange', () => {
931                    let elements = st.assignedElements();
932                    if (elements.length > 0) {
933                        this.slotShowTotal = elements[0];
934                    }
935                    if (this.slotShowTotal) {
936                        let cloneNode = this.slotShowTotal.render({
937                            total: this._total,
938                            range: [1, this._pages],
939                        }).content.cloneNode(true);
940                        this.shadowRoot.querySelector('#showTotal').append(cloneNode);
941                    }
942                })
943                this.leftArrow = this.shadowRoot.querySelector('.left-arrow');
944                this.first = this.shadowRoot.querySelector('.first');
945                this.doubleLeft = this.shadowRoot.querySelector('.double-left');
946                this.one = this.shadowRoot.querySelector('.one');
947                this.two = this.shadowRoot.querySelector('.two');
948                this.three = this.shadowRoot.querySelector('.three');
949                this.four = this.shadowRoot.querySelector('.four');
950                this.five = this.shadowRoot.querySelector('.five');
951                this.doubleRight = this.shadowRoot.querySelector('.double-right');
952                this.last = this.shadowRoot.querySelector('.last');
953                this.rightArrow = this.shadowRoot.querySelector('.right-arrow');
954                this.pagesChange = this.shadowRoot.querySelector('.pages-change');
955                this.jump = this.shadowRoot.querySelector('.jump');
956                this.nodes = [this.one, this.two, this.three, this.four, this.five]
957
958                let jsonArray = JSON.parse(this.pageSizeOptions);
959                if (jsonArray) {
960                    this.pagesChange.dataSource = jsonArray.map(a => {
961                        return { key: a, val: `${a} 条/页`, }
962                    });
963                    this.pagesChange.value = jsonArray[0] + '';
964                    this.pageSize = jsonArray[0] + '';
965                } else {
966                    this.pagesChange.dataSource = [10, 20, 50, 100].map(a => {
967                        return { key: a, val: `${a} 条/页`, }
968                    });
969                    this.pagesChange.value = '10';
970                    this.pageSize = '10';
971                }
972
973                this.jump.addEventListener('onPressEnter', e => {
974                    if (e.target.value) {
975                        this.current = e.target.value;
976                        e.target.value = '';
977                        this.match();
978                    }
979                });
980                this.pagesChange.onchange = e => {
981                    // console.log(e.detail);
982                    this.pageSize = e.detail.value;
983                    this.match();
984                    if (this.slotShowTotal) {
985                        let cloneNode = this.slotShowTotal.render({
986                            total: this._total,
987                            range: [1, this._pages],
988                        }).content.cloneNode(true);
989                        this.shadowRoot.querySelector('#showTotal').lastElementChild.remove();
990                        this.shadowRoot.querySelector('#showTotal').append(cloneNode);
991                    }
992                    this.dispatchEvent(new CustomEvent('onShowSizeChange', {
993                        detail: {
994                            current: parseInt(this.current),
995                            size: parseInt(this.pageSize)
996                        }
997                    }))
998                }
999
1000                this.leftArrow.onclick = (e) => {
1001                    if (this._current > 1) {
1002                        this.current = `${this._current - 1}`
1003                        this.match();
1004                    }
1005                }
1006                this.rightArrow.onclick = (e) => {
1007                    if (this._current < this._pages) {
1008                        this.current = `${this._current + 1}`
1009                        this.match();
1010                    }
1011                }
1012                let nodeClickHandler = e => {
1013                    this.current = `${e.currentTarget.val}`;
1014                    this.match();
1015                };
1016                this.first.onclick = nodeClickHandler;
1017                this.nodes.forEach(a => a.onclick = nodeClickHandler);
1018                this.last.onclick = nodeClickHandler;
1019                this.doubleLeft.onmouseover = e => {
1020                    e.target.name = 'doubleleft';
1021                }
1022                this.doubleLeft.onmouseout = e => {
1023                    e.target.name = 'ellipsis'
1024                }
1025                this.doubleLeft.onclick = e => {
1026                    let k = this._current - 5;
1027                    if (k < 1) k = 1;
1028                    this.current = `${k}`;
1029                    this.match();
1030                }
1031                this.doubleRight.onmouseover = e => {
1032                    e.target.name = 'doubleright';
1033                }
1034                this.doubleRight.onmouseout = e => {
1035                    e.target.name = 'ellipsis'
1036                }
1037                this.doubleRight.onclick = e => {
1038                    let k = this._current + 5;
1039                    if (k > this._pages) k = this._pages;
1040                    this.current = `${k}`;
1041                    this.match();
1042                }
1043                this.match();
1044            }
1045
1046            match() {
1047                this._current = parseInt(this.current);
1048                this._total = parseInt(this.total);
1049                this._pageSize = parseInt(this.pageSize);
1050                this._pages = Math.ceil(this._total / this._pageSize)
1051                if (this._current > this._pages) {
1052                    this._current = this._pages;
1053                    this.current = `${this._pages}`;
1054                } else if (this._current < 1) {
1055                    this._current = 1;
1056                    this.current = `1`;
1057                }
1058                //是否展示 pageSize 切换器,当 total 大于 50 时默认为 true
1059                // if(this._total>50) this.setAttribute('show-size-changer', '');
1060
1061                // console.log(`${this._total} ${this._current}/${this._pages} ${this._pageSize}`);
1062                if (this._current === 1) {
1063                    this.leftArrow.setAttribute('disable', '');
1064                } else {
1065                    this.leftArrow.removeAttribute('disable');
1066                }
1067                if (this._current === this._pages) {
1068                    this.rightArrow.setAttribute('disable', '');
1069                } else {
1070                    this.rightArrow.removeAttribute('disable');
1071                }
1072                this.hiddenNode(this.first, this.last, this.one, this.two, this.three, this.four, this.five, this.doubleLeft, this.doubleRight)
1073                if (this._pages <= 5) {
1074                    for (let i = 0; i < this._pages; i++) {
1075                        this.displayNode(this.nodes[i]);
1076                        this.item(this.nodes[i], i + 1);
1077                    }
1078                } else if (this._pages === 6) {
1079                    this.displayNode(this.first);
1080                    this.item(this.first, 1);
1081                    for (let i = 0; i < this._pages; i++) {
1082                        this.displayNode(this.nodes[i]);
1083                        this.item(this.nodes[i], i + 2);
1084                    }
1085                } else {
1086                    this.displayNode(this.one, this.two, this.three, this.four, this.five)
1087                    if (this._current - 3 > 1 && this._current + 3 < this._pages) { // 左边溢出 右边溢出
1088                        this.displayNode(this.first, this.last, this.doubleLeft, this.doubleRight);
1089                        this.item(this.first, 1);
1090                        this.item(this.last, this._pages);
1091                        this.item(this.one, this._current - 2);
1092                        this.item(this.two, this._current - 1);
1093                        this.item(this.three, this._current);
1094                        this.item(this.four, this._current + 1);
1095                        this.item(this.five, this._current + 2);
1096                    } else if (this._current - 3 === 1 && this._current + 3 < this._pages) {//左边刚好 右边溢出
1097                        this.displayNode(this.first, this.last, this.doubleRight);
1098                        this.item(this.first, 1)
1099                        this.item(this.last, this._pages)
1100                        for (let i = 0; i < this.nodes.length; i++) {
1101                            this.item(this.nodes[i], i + 2);
1102                        }
1103                    } else if (this._current - 3 < 1 && this._current + 3 < this._pages) {  //左边不够 右边溢出
1104                        this.displayNode(this.last, this.doubleRight);
1105                        this.item(this.last, this._pages)
1106                        for (let i = 0; i < this.nodes.length; i++) {
1107                            this.item(this.nodes[i], i + 1);
1108                        }
1109                    } else if (this._current - 3 > 1 && this._current + 3 === this._pages) {// 左边溢出 右边刚好
1110                        this.displayNode(this.first, this.last, this.doubleLeft);
1111                        this.item(this.first, 1);
1112                        this.item(this.last, this._pages);
1113                        this.item(this.nodes[0], this._pages - 5);
1114                        this.item(this.nodes[1], this._pages - 4);
1115                        this.item(this.nodes[2], this._pages - 3);
1116                        this.item(this.nodes[3], this._pages - 2);
1117                        this.item(this.nodes[4], this._pages - 1);
1118                    } else if (this._current - 3 === 1 && this._current + 3 === this._pages) {// 左边刚好 右边刚好
1119                        this.displayNode(this.first, this.last);
1120                        this.item(this.first, 1);
1121                        for (let i = 0; i < this._pages; i++) {
1122                            this.displayNode(this.nodes[i]);
1123                            this.item(this.nodes[i], i + 2);
1124                        }
1125                        this.item(this.last, 7);
1126                    } else if (this._current - 3 < 1 && this._current + 3 === this._pages) {// 左边不够 右边刚好
1127                        this.displayNode(this.last);
1128                        this.item(this.last, this._pages)
1129                        for (let i = 0; i < this.nodes.length; i++) {
1130                            this.item(this.nodes[i], i + 1);
1131                        }
1132                    } else if (this._current - 3 > 1 && this._current + 3 > this._pages) { //左边溢出 右边不够
1133                        this.displayNode(this.first, this.doubleLeft)
1134                        this.item(this.first, 1);
1135                        this.item(this.nodes[0], this._pages - 4);
1136                        this.item(this.nodes[1], this._pages - 3);
1137                        this.item(this.nodes[2], this._pages - 2);
1138                        this.item(this.nodes[3], this._pages - 1);
1139                        this.item(this.nodes[4], this._pages - 0);
1140                    } else if (this._current - 3 === 1 && this._current + 3 > this._pages) { //左边刚好 右边不够
1141                        this.displayNode(this.first);
1142                        this.item(this.first, 1);
1143                        this.item(this.nodes[0], this._pages - 4);
1144                        this.item(this.nodes[1], this._pages - 3);
1145                        this.item(this.nodes[2], this._pages - 2);
1146                        this.item(this.nodes[3], this._pages - 1);
1147                        this.item(this.nodes[4], this._pages - 0);
1148                    } else if (this._current - 3 < 1 && this._current + 3 > this._pages) { //左边不够 右边不够
1149                        //限定了 pages>6 这种情况不存在
1150                    }
1151                }
1152            }
1153
1154            item(el, val) {
1155                el.val = val;
1156                el.textContent = val;
1157                if (val === this._current) {
1158                    el.setAttribute('selected', '');
1159                } else {
1160                    el.removeAttribute('selected');
1161                }
1162            }
1163
1164            displayNode() {
1165                [...arguments].forEach(a => a.style.display = 'flex');
1166            }
1167
1168            hiddenNode(n) {
1169                [...arguments].forEach(a => a.style.display = 'none');
1170            }
1171
1172            //当 custom element从文档DOM中删除时,被调用。
1173            disconnectedCallback() {
1174            }
1175
1176            //当 custom element被移动到新的文档时,被调用。
1177            adoptedCallback() {
1178            }
1179
1180            //当 custom element增加、删除、修改自身属性时,被调用。
1181            attributeChangedCallback(name, oldValue, newValue) {
1182                if (name === 'current') {
1183                    this.dispatchEvent(new CustomEvent('onChange', {
1184                        detail: {
1185                            page: parseInt(newValue),
1186                            pageSize: this._pageSize
1187                        }
1188                    }));
1189                } else if (name === 'total') {
1190                    this.dispatchEvent(new CustomEvent('onChange', {
1191                        detail: {
1192                            total: parseInt(newValue),
1193                            current: this._current,
1194                            pageSize: this._pageSize
1195                        }
1196                    }));
1197                    this.match()
1198                }
1199
1200            }
1201        }
1202        if (!customElements.get('lit-pagination')) {
1203            customElements.define('lit-pagination', LitPagination);
1204        }
1205
1206        class LitSelect extends HTMLElement {
1207            static get observedAttributes() {
1208                return [
1209                    'value',//默认值
1210                    'default-value',//默认值
1211                    'placeholder',//placeholder
1212                    'disabled',
1213                    'loading',//是否处于加载状态
1214                    'allow-clear',//是否允许清除
1215                    'show-search',//是否允许搜索
1216                    'list-height',//设置弹窗滚动高度 默认256px
1217                    'border',//是否显示边框
1218                    'mode',// mode='multiple'多选
1219                ];
1220            }
1221
1222            get value() {
1223                return this.getAttribute('value') || this.defaultValue;
1224            }
1225
1226            set value(value) {
1227                this.setAttribute('value', value);
1228            }
1229
1230            get border() {
1231                return this.getAttribute('border') || 'true';
1232            }
1233
1234            set border(value) {
1235                if (value) {
1236                    this.setAttribute('border', 'true');
1237                } else {
1238                    this.setAttribute('border', 'false');
1239                }
1240            }
1241
1242            get listHeight() {
1243                return this.getAttribute('list-height') || '256px';
1244            }
1245
1246            set listHeight(value) {
1247                this.setAttribute('list-height', value);
1248            }
1249
1250            get defaultPlaceholder() {
1251                return this.getAttribute('placeholder') || '请选择';
1252            }
1253
1254            get showSearch() {
1255                return this.hasAttribute('show-search');
1256            }
1257
1258            set defaultValue(value) {
1259                this.setAttribute('default-value', value);
1260            }
1261
1262            get defaultValue() {
1263                return this.getAttribute('default-value') || '';
1264            }
1265
1266            set placeholder(value) {
1267                this.setAttribute('placeholder', value);
1268            }
1269
1270            get placeholder() {
1271                return this.getAttribute('placeholder') || this.defaultPlaceholder;
1272            }
1273
1274            get loading() {
1275                return this.hasAttribute('loading');
1276            }
1277
1278            set loading(value) {
1279                if (value) {
1280                    this.setAttribute('loading', '');
1281                } else {
1282                    this.removeAttribute('loading')
1283                }
1284            }
1285
1286            constructor() {
1287                super();
1288                const shadowRoot = this.attachShadow({ mode: 'open' });
1289                shadowRoot.innerHTML = `
1290        <style>
1291:host{
1292    display: inline-flex;
1293    position: relative;
1294    overflow: visible;
1295    cursor: pointer;
1296    transition: all .3s;
1297    border-radius: 2px;
1298    outline: none;
1299    -webkit-user-select:none ;
1300    -moz-user-select:none;
1301    user-select:none;
1302    /*width: 100%;*/
1303}
1304:host(:not([border])),
1305:host([border='true']){
1306    border: 1px solid #dcdcdc;
1307}
1308input{
1309    border: 0;
1310    outline: none;
1311    background-color: transparent;
1312    cursor: pointer;
1313    transition: all .3s;
1314    -webkit-user-select:none ;
1315    -moz-user-select:none;
1316    user-select:none;
1317    display: inline-flex;
1318}
1319:host(:not([mode]))  input{
1320    width: 100%;
1321}
1322:host([mode])  input{
1323    padding: 6px 0px;
1324}
1325:host([mode])  .root{
1326    padding: 1px 8px;
1327}
1328.root{
1329    position: relative;
1330    padding: 6px 8px;
1331    display: flex;
1332    align-items: center;
1333    justify-content: space-between;
1334    transition: all .3s;
1335    border-radius: 2px;
1336    background-color: #fff;
1337    outline: none;
1338    font-size: 1rem;
1339    z-index: 2;
1340    -webkit-user-select:none ;
1341    -moz-user-select:none;
1342    user-select:none;
1343    width: 100%;
1344}
1345.body{
1346    max-height: ${this.listHeight};
1347    position: absolute;
1348    top: 100%;
1349    z-index: 99;
1350    padding-top: 5px;
1351    margin-top: 2px;
1352    background-color: #fff;
1353    width: 100%;
1354    transition: all 0.2s;
1355    transform: scaleY(.6);
1356    visibility: hidden;
1357    opacity: 0;
1358    transform-origin: top center;
1359    display: block;
1360    flex-direction: column;
1361    box-shadow: 0 5px 15px 0px #00000033;
1362    border-radius: 2px;
1363    overflow: auto;
1364}
1365.icon{
1366    pointer-events: none;
1367}
1368.noSelect{
1369  -webkit-touch-callout:none; /* iOS Safari */
1370  -webkit-user-select:none;   /* Chrome/Safari/Opera */
1371  -khtml-user-select:none;    /* Konqueror */
1372  -moz-user-select:none;      /* Firefox */
1373  -ms-user-select:none;       /* Internet Explorer/Edge */
1374  user-select:none;           /* Non-prefixed version */
1375}
1376
1377:host(:not([border]):not([disabled]):focus),
1378:host([border='true']:not([disabled]):focus),
1379:host(:not([border]):not([disabled]):hover),
1380:host([border='true']:not([disabled]):hover){
1381    border:1px solid #42b983
1382}
1383:host(:not([disabled]):focus) .body,
1384:host(:not([disabled]):focus-within) .body{
1385    transform: scaleY(1);
1386    opacity: 1;
1387    z-index: 99;
1388    visibility: visible;
1389}
1390:host(:not([disabled]):focus)  input{
1391    color: #bebebe;
1392}
1393:host(:not([border])[disabled]) *,
1394:host([border='true'][disabled]) *{
1395    background-color: #f5f5f5;
1396    color: #b7b7b7;
1397    cursor: not-allowed;
1398}
1399:host([border='false'][disabled]) *{
1400    color: #b7b7b7;
1401    cursor: not-allowed;
1402}
1403:host([loading]) .loading{
1404    display: flex;
1405}
1406:host([loading]) .icon{
1407    display: none;
1408}
1409:host(:not([loading])) .loading{
1410    display: none;
1411}
1412:host(:not([loading])) .icon{
1413    display: flex;
1414}
1415:host(:not([allow-clear])) .clear{
1416    display: none;
1417}
1418.clear{
1419    display: none;
1420    color: #bfbfbf;
1421}
1422.clear:hover{
1423    color: #8c8c8c;
1424}
1425.search{
1426    display: none;
1427    color: #bfbfbf;
1428}
1429.multipleRoot{
1430    display: flex;
1431    flex-direction: column;
1432    flex-wrap: wrap;
1433    flex-flow: wrap;
1434    align-items: center;
1435    width: 100%;
1436}
1437.tag{
1438    display: inline-flex;
1439    align-items: center;
1440    background-color: #f5f5f5;
1441    padding: 1px 4px;
1442    height: auto;
1443    font-size: .75rem;
1444    font-weight: bold;
1445    color: #242424;
1446    overflow: auto;
1447    position: relative;
1448    margin-right: 4px;
1449    margin-top: 1px;
1450    margin-bottom: 1px;
1451}
1452.tag-close{
1453    font-size: .8rem;
1454    padding: 2px;
1455    margin-left: 0px;
1456    color: #999999;
1457}
1458.tag-close:hover{
1459    color: #333;
1460}
1461
1462</style>
1463<div class="root noSelect" tabindex="0" hidefocus="true">
1464    <div class="multipleRoot">
1465        <input placeholder="${this.placeholder}" style="" autocomplete="off" ${this.showSearch ? '' : 'readonly'} tabindex="0">
1466    </div><!--多选-->
1467    <lit-loading class="loading" size="12"></lit-loading>
1468    <!--<lit-icon class="icon" name='down' color="#c3c3c3"></lit-icon>-->
1469    <svg class="icon" viewBox="0 0 1024 1024" aria-hidden="true" style="width: 16px;width: 16px;fill: #c3c3c3">
1470        <path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3 0.1-12.7-6.4-12.7z"></path>
1471    </svg>
1472<!--    <lit-icon class="clear" name='close-circle-fill'></lit-icon>-->
1473    <svg class="clear" viewBox="0 0 1024 1024" aria-hidden="true" style="width: 16px;width: 16px;">
1474        <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64z m165.4 618.2l-66-0.3L512 563.4l-99.3 118.4-66.1 0.3c-4.4 0-8-3.5-8-8 0-1.9 0.7-3.7 1.9-5.2l130.1-155L340.5 359c-1.2-1.5-1.9-3.3-1.9-5.2 0-4.4 3.6-8 8-8l66.1 0.3L512 464.6l99.3-118.4 66-0.3c4.4 0 8 3.5 8 8 0 1.9-0.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path>
1475    </svg>
1476<!--    <lit-icon class="search" name='search'></lit-icon>-->
1477    <svg class="search" viewBox="0 0 1024 1024" aria-hidden="true" style="width: 16px;width: 16px;">
1478        <path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6c3.2 3.2 8.4 3.2 11.6 0l43.6-43.5c3.2-3.2 3.2-8.4 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path>
1479    </svg>
1480</div>
1481<div class="body">
1482    <slot></slot>
1483    <slot name="footer"></slot>
1484</div>
1485        `
1486            }
1487
1488            isMultiple() {
1489                return this.hasAttribute('mode') && this.getAttribute('mode') === 'multiple'
1490            }
1491
1492            newTag(value, text) {
1493                let tag = document.createElement('div');
1494                let icon = document.createElement('lit-icon');
1495                icon.classList.add('tag-close')
1496                icon.name = 'close'
1497                let span = document.createElement('span');
1498                tag.classList.add('tag');
1499                span.dataset['value'] = value;
1500                span.textContent = text;
1501                tag.append(span);
1502                tag.append(icon);
1503                icon.onclick = ev => {
1504                    tag.parentElement.removeChild(tag);
1505                    this.querySelector(`lit-select-option[value=${value}]`).removeAttribute('selected')
1506                    if (this.shadowRoot.querySelectorAll('.tag').length == 0) {
1507                        this.inputElement.style.width = 'auto';
1508                        this.inputElement.placeholder = this.defaultPlaceholder;
1509                    }
1510                    ev.stopPropagation();
1511                }
1512                tag.value = value;
1513                tag.dataset['value'] = value;
1514                tag.text = text;
1515                tag.dataset['text'] = text;
1516                return tag;
1517            }
1518
1519            //当 custom element首次被插入文档DOM时,被调用。
1520            connectedCallback() {
1521                this.tabIndex = 0;//设置当前组件为可以获取焦点
1522                this.focused = false;
1523                this.inputElement = this.shadowRoot.querySelector('input');
1524                this.inputElement.style.width = '100%'
1525                this.clearElement = this.shadowRoot.querySelector('.clear');
1526                this.iconElement = this.shadowRoot.querySelector('.icon');
1527                this.searchElement = this.shadowRoot.querySelector('.search');
1528                this.multipleRootElement = this.shadowRoot.querySelector('.multipleRoot');
1529                // console.log(this.multipleRootElement);
1530                //点击清理 清空input值,展示placeholder,
1531                this.clearElement.onclick = ev => {
1532                    if (this.isMultiple()) {
1533                        let delNodes = []
1534                        this.multipleRootElement.childNodes.forEach(a => {
1535                            if (a.tagName === 'DIV') {
1536                                delNodes.push(a);
1537                            }
1538                        })
1539                        for (let i = 0; i < delNodes.length; i++) {
1540                            delNodes[i].remove();
1541                        }
1542                        if (this.shadowRoot.querySelectorAll('.tag').length == 0) {
1543                            this.inputElement.style.width = 'auto';
1544                            this.inputElement.placeholder = this.defaultPlaceholder;
1545                        }
1546                    }
1547                    this.querySelectorAll('lit-select-option').forEach(a => a.removeAttribute('selected'));
1548                    this.inputElement.value = ''
1549                    this.clearElement.style.display = 'none';
1550                    this.iconElement.style.display = 'flex';
1551                    this.blur();
1552                    ev.stopPropagation();//这里不会因为点击清理而触发 选择栏目显示或者隐藏
1553                    this.dispatchEvent(new CustomEvent('onClear', { detail: ev }))//向外派发清理事件
1554                }
1555                //初始化时遍历所有的option节点
1556                this.initOptions();
1557                //当前控件点击时 如果时select本身 需要显示 或 隐藏选择栏目,通过this.focused变量控制(默认为false)
1558                this.onclick = ev => {
1559                    if (ev.target.tagName === 'LIT-SELECT') {
1560                        if (this.focused === false) {
1561                            this.inputElement.focus();
1562                            this.focused = true;
1563                        } else {
1564                            this.blur();
1565                            this.focused = false;
1566                        }
1567                    }
1568                }
1569                this.onmouseover = this.onfocus = ev => {
1570                    // console.log('onmouseover',ev);
1571                    if (this.hasAttribute('allow-clear')) {
1572                        if (this.inputElement.value.length > 0 || this.inputElement.placeholder !== this.defaultPlaceholder) {
1573                            this.clearElement.style.display = 'flex'
1574                            this.iconElement.style.display = 'none';
1575                        } else {
1576                            this.clearElement.style.display = 'none'
1577                            this.iconElement.style.display = 'flex';
1578                        }
1579                    }
1580                }
1581                this.onmouseout = this.onblur = ev => {
1582                    // console.log('onmouseout',ev);
1583                    if (this.hasAttribute('allow-clear')) {
1584                        this.clearElement.style.display = 'none';
1585                        this.iconElement.style.display = 'flex';
1586                    }
1587                    this.focused = false;
1588                }
1589                //输入框获取焦点时,value值 暂存于 placeholder  然后value值清空,这样值会以placeholder形式灰色展示,鼠标位于第一个字符
1590                this.inputElement.onfocus = ev => {
1591                    if (this.hasAttribute('disabled')) return;//如果控件处于disabled状态 直接忽略
1592                    if (this.inputElement.value.length > 0) {
1593                        this.inputElement.placeholder = this.inputElement.value;
1594                        this.inputElement.value = ''
1595                    }
1596                    if (this.hasAttribute('show-search')) {//如果有show-search属性 需要显示放大镜,隐藏向下的箭头
1597                        this.searchElement.style.display = 'flex';
1598                        this.iconElement.style.display = 'none';
1599                    }
1600                    this.querySelectorAll('lit-select-option').forEach(a => {//input获取焦点时显示所有可选项,相当于清理了搜索结果
1601                        a.style.display = 'flex';
1602                    })
1603                }
1604                //当输入框失去焦点的时候 placeholder 的值 保存到value上,input显示值
1605                this.inputElement.onblur = ev => {
1606                    if (this.hasAttribute('disabled')) return;//如果控件处于disabled状态 直接忽略
1607                    if (this.isMultiple()) {
1608                        if (this.hasAttribute('show-search')) {//如果有show-search属性 失去焦点需要 隐藏放大镜图标,显示默认的向下箭头图标
1609                            this.searchElement.style.display = 'none';
1610                            this.iconElement.style.display = 'flex';
1611                        }
1612                    } else {
1613                        if (this.inputElement.placeholder !== this.defaultPlaceholder) {//如果placeholder为 请输入(默认值)不做处理
1614                            this.inputElement.value = this.inputElement.placeholder; //placeholder 保存的值放入 value中
1615                            this.inputElement.placeholder = this.defaultPlaceholder;//placeholder 值为 默认值(请输入)
1616                        }
1617                        if (this.hasAttribute('show-search')) {//如果有show-search属性 失去焦点需要 隐藏放大镜图标,显示默认的向下箭头图标
1618                            this.searchElement.style.display = 'none';
1619                            this.iconElement.style.display = 'flex';
1620                        }
1621                    }
1622                }
1623                //输入框每次文本变化 会匹配搜索的option 显示或者隐藏,达到搜索的效果
1624                this.inputElement.oninput = ev => {
1625                    // console.log(ev.target.value);
1626                    let els = [...this.querySelectorAll('lit-select-option')];
1627                    if (!ev.target.value) {
1628                        els.forEach(a => a.style.display = 'flex');
1629                    } else {
1630                        els.forEach(a => {
1631                            let value = a.getAttribute('value');
1632                            // console.log(value.toLowerCase(), ev.target.value.toLowerCase());
1633                            if (value.toLowerCase().indexOf(ev.target.value.toLowerCase()) !== -1 ||
1634                                a.textContent.toLowerCase().indexOf(ev.target.value.toLowerCase()) !== -1) {
1635                                a.style.display = 'flex';
1636                            } else {
1637                                a.style.display = 'none';
1638                            }
1639                        })
1640                    }
1641                }
1642                //输入框按下回车键,自动输入当前搜索出来的第一行,及display!='none'的第一个,搜索会隐藏其他行
1643                this.inputElement.onkeydown = ev => {
1644                    if (ev.key === 'Backspace') {
1645                        if (this.isMultiple()) {
1646                            let tag = this.multipleRootElement.lastElementChild.previousElementSibling;
1647                            if (tag) {
1648                                console.log(tag.value);
1649                                this.querySelector(`lit-select-option[value=${tag.value}]`).removeAttribute('selected');
1650                                tag.remove()
1651                                if (this.shadowRoot.querySelectorAll('.tag').length == 0) {
1652                                    this.inputElement.style.width = 'auto';
1653                                    this.inputElement.placeholder = this.defaultPlaceholder;
1654                                }
1655                            }
1656                        } else {
1657                            this.clear();
1658                            this.dispatchEvent(new CustomEvent('onClear', { detail: ev }))//向外派发清理事件
1659                        }
1660                    } else if (ev.key === 'Enter') {
1661                        let filter = [...this.querySelectorAll('lit-select-option')].filter(a => a.style.display !== 'none');
1662                        if (filter.length > 0) {
1663                            this.inputElement.value = filter[0].textContent;
1664                            this.inputElement.placeholder = filter[0].textContent;
1665                            this.blur();
1666                            this.dispatchEvent(new CustomEvent('change', {
1667                                detail: {
1668                                    selected: true,
1669                                    value: filter[0].getAttribute('value'),
1670                                    text: filter[0].textContent
1671                                }
1672                            }));//向外层派发change事件,返回当前选中项
1673                        }
1674                    }
1675                }
1676            }
1677
1678            initOptions() {
1679                this.querySelectorAll('lit-select-option').forEach(a => {
1680                    //如果节点的值为 当前控件的默认值 defalut-value则 显示该值对应的option文本
1681                    if (this.isMultiple()) {
1682                        a.setAttribute('check', '');
1683                        if (a.getAttribute('value') === this.defaultValue) {
1684                            let tag = this.newTag(a.getAttribute('value'), a.textContent);
1685                            this.multipleRootElement.insertBefore(tag, this.inputElement);
1686                            this.inputElement.placeholder = '';
1687                            this.inputElement.value = '';
1688                            this.inputElement.style.width = '1px';
1689                            a.setAttribute('selected', '');
1690                        }
1691                        // this.inputElement.focus();
1692                    } else {
1693                        if (a.getAttribute('value') === this.defaultValue) {
1694                            this.inputElement.value = a.textContent;
1695                            a.setAttribute('selected', '');
1696                        }
1697                    }
1698                    //每个option设置onSelected事件 接受当前点击的option
1699                    a.addEventListener('onSelected', (e) => {
1700                        // console.log(e.detail);
1701                        //所有option设置为未选中状态
1702                        if (this.isMultiple()) {//多选
1703                            if (a.hasAttribute('selected')) {
1704                                // console.log(e.detail.value);
1705                                let tag = this.shadowRoot.querySelector(`div[data-value=${e.detail.value}]`);
1706                                // console.log(tag);
1707                                tag.parentElement.removeChild(tag);
1708                                e.detail.selected = false;
1709                            } else {
1710                                let tag = this.newTag(e.detail.value, e.detail.text);
1711                                this.multipleRootElement.insertBefore(tag, this.inputElement);
1712                                this.inputElement.placeholder = '';
1713                                this.inputElement.value = '';
1714                                this.inputElement.style.width = '1px';
1715                            }
1716                            if (this.shadowRoot.querySelectorAll('.tag').length == 0) {
1717                                this.inputElement.style.width = 'auto';
1718                                this.inputElement.placeholder = this.defaultPlaceholder;
1719                            }
1720                            this.inputElement.focus();
1721                        } else {//单选
1722                            [...this.querySelectorAll('lit-select-option')].forEach(a => a.removeAttribute('selected'))
1723                            this.blur();//失去焦点,隐藏选择栏目列表
1724                            this.inputElement.value = e.detail.text;
1725                        }
1726                        //设置当前option为选择状态
1727                        if (a.hasAttribute('selected')) {
1728                            a.removeAttribute('selected')
1729                        } else {
1730                            a.setAttribute('selected', '')
1731                        }
1732                        //设置input的值为当前选择的文本
1733                        this.dispatchEvent(new CustomEvent('change', { detail: e.detail }));//向外层派发change事件,返回当前选中项
1734                    })
1735                })
1736            }
1737
1738            //js调用清理选项
1739            clear() {
1740                this.inputElement.value = '';
1741                this.inputElement.placeholder = this.defaultPlaceholder;
1742            }
1743
1744            //重置为默认值
1745            reset() {
1746                this.querySelectorAll('lit-select-option').forEach(a => {
1747                    //如果节点的值为 当前控件的默认值 defalut-value则 显示该值对应的option文本
1748                    [...this.querySelectorAll('lit-select-option')].forEach(a => a.removeAttribute('selected'))
1749                    if (a.getAttribute('value') === this.defaultValue) {
1750                        this.inputElement.value = a.textContent;
1751                        a.setAttribute('selected', '');
1752                    }
1753                })
1754            }
1755
1756            //当 custom element从文档DOM中删除时,被调用。
1757            disconnectedCallback() {
1758
1759            }
1760
1761            //当 custom element被移动到新的文档时,被调用。
1762            adoptedCallback() {
1763                console.log('Custom square element moved to new page.');
1764            }
1765
1766            //当 custom element增加、删除、修改自身属性时,被调用。
1767            attributeChangedCallback(name, oldValue, newValue) {
1768                if (name === 'value' && this.inputElement) {
1769                    if (newValue) {
1770                        [...this.querySelectorAll('lit-select-option')].forEach(a => {
1771                            if (a.getAttribute('value') === newValue) {
1772                                a.setAttribute('selected', '');
1773                                this.inputElement.value = a.textContent;
1774                            } else {
1775                                a.removeAttribute('selected')
1776                            }
1777                        })
1778                    } else {
1779                        this.clear();
1780                    }
1781                }
1782            }
1783
1784            set dataSource(value) {
1785                value.forEach(a => {
1786                    let option = document.createElement('lit-select-option');
1787                    option.setAttribute('value', a.key);
1788                    option.textContent = a.val;
1789                    this.append(option)
1790                })
1791                this.initOptions();
1792            }
1793
1794        }
1795        if (!customElements.get('lit-select')) {
1796            customElements.define('lit-select', LitSelect);
1797        }
1798
1799        class LitSelectGroup extends HTMLElement {
1800            static get observedAttributes() {
1801                return ['label']
1802            }
1803
1804            get label() {
1805                return this.getAttribute('label') || '';
1806            }
1807
1808            set label(value) {
1809                this.setAttribute('label', value);
1810            }
1811
1812            constructor() {
1813                super();
1814                const shadowRoot = this.attachShadow({ mode: 'open' });
1815                shadowRoot.innerHTML = `
1816        <style>
1817        :host{
1818            display: flex;
1819            flex-direction: column;
1820            /*padding-left: 10px;*/
1821        }
1822        .lab{
1823            padding: 8px 10px 8px 10px;
1824            font-size: .5rem;
1825            color: #8c8c8c;
1826        }
1827        ::slotted(lit-select-option){
1828            padding-left: 20px;
1829        }
1830        </style>
1831        <div class="lab">${this.label}</div>
1832        <slot></slot>
1833        `
1834            }
1835
1836            //当 custom element首次被插入文档DOM时,被调用。
1837            connectedCallback() {
1838
1839            }
1840
1841            //当 custom element从文档DOM中删除时,被调用。
1842            disconnectedCallback() {
1843
1844            }
1845
1846            //当 custom element被移动到新的文档时,被调用。
1847            adoptedCallback() {
1848                console.log('Custom square element moved to new page.');
1849            }
1850
1851            //当 custom element增加、删除、修改自身属性时,被调用。
1852            attributeChangedCallback(name, oldValue, newValue) {
1853
1854            }
1855        }
1856        if (!customElements.get('lit-select-group')) {
1857            customElements.define('lit-select-group', LitSelectGroup);
1858        }
1859
1860        class LitSelectOption extends HTMLElement {
1861            static get observedAttributes() {
1862                return ['selected', 'disabled', 'check']
1863            }
1864
1865            constructor() {
1866                super();
1867                const shadowRoot = this.attachShadow({ mode: 'open' });
1868                shadowRoot.innerHTML = `
1869        <style>
1870        :host{
1871            display: flex;
1872            padding: 8px 10px;
1873            transition: all .3s;
1874            color: #333;
1875            tab-index: -1;
1876            overflow: scroll;
1877            align-items: center;
1878            justify-content: space-between;
1879        }
1880        :host(:not([disabled])[selected]){
1881            background-color: #e9f7fe;
1882            font-weight: bold;
1883        }
1884        :host(:not([disabled]):not([selected]):hover){
1885            background-color: #f5f5f5;
1886        }
1887        :host([disabled]){
1888            cursor: not-allowed;
1889            color: #bfbfbf;
1890        }
1891        :host([selected][check]) .check{
1892             display: flex;
1893        }
1894        :host(:not([selected])) .check{
1895             display: none;
1896        }
1897        :host(:not([check])) .check{
1898            display: none;
1899        }
1900        </style>
1901        <slot></slot>
1902        <lit-icon class="check" name="check"></lit-icon>
1903        `
1904            }
1905
1906            //当 custom element首次被插入文档DOM时,被调用。
1907            connectedCallback() {
1908                if (!this.hasAttribute('disabled')) {
1909                    this.onclick = ev => {
1910                        this.dispatchEvent(new CustomEvent('onSelected', {
1911                            detail: {
1912                                selected: true,
1913                                value: this.getAttribute('value'),
1914                                text: this.textContent
1915                            }
1916                        }))
1917                    }
1918                }
1919
1920            }
1921
1922            //当 custom element从文档DOM中删除时,被调用。
1923            disconnectedCallback() {
1924
1925            }
1926
1927            //当 custom element被移动到新的文档时,被调用。
1928            adoptedCallback() {
1929                console.log('Custom square element moved to new page.');
1930            }
1931
1932            //当 custom element增加、删除、修改自身属性时,被调用。
1933            attributeChangedCallback(name, oldValue, newValue) {
1934
1935            }
1936        }
1937        if (!customElements.get('lit-select-option')) {
1938            customElements.define('lit-select-option', LitSelectOption);
1939        }
1940
1941        class LitTable extends HTMLElement {
1942            static get observedAttributes() {
1943                return ['scroll-y', 'selectable', 'defaultOrderColumn']
1944            }
1945
1946            get selectable() {
1947                return this.hasAttribute('selectable');
1948            }
1949
1950            set selectable(value) {
1951                if (value) {
1952                    this.setAttribute('selectable', '');
1953                } else {
1954                    this.removeAttribute('selectable');
1955                }
1956            }
1957
1958            get scrollY() {
1959                return this.getAttribute('scroll-y') || 'auto';
1960            }
1961
1962            set scrollY(value) {
1963                this.setAttribute('scroll-y', value);
1964            }
1965
1966            get dataSource() {
1967                return this.ds || [];
1968            }
1969
1970            set dataSource(value) {
1971                this.ds = value;
1972                if (this.hasAttribute('tree')) {
1973                    this.renderTreeTable();
1974                } else {
1975                    this.renderTable();
1976                }
1977            }
1978
1979            constructor() {
1980                super();
1981                const shadowRoot = this.attachShadow({ mode: 'open' });
1982                shadowRoot.innerHTML = `
1983<style>
1984:host{
1985    display: grid;
1986    grid-template-columns: repeat(1,1fr);
1987    overflow: auto;
1988    /*width: 500px;*/
1989    width: 100%;
1990    height: 100%;
1991}
1992.tr{
1993    display: grid;
1994    transition: all .3s;
1995}
1996.tr:nth-of-type(even){
1997    background-color: #fcfcfc;
1998}
1999/*.tr:not(:last-of-type):not(:first-of-type){*/
2000/*    border-top: 1px solid #f0f0f0;*/
2001/*}*/
2002/*.tr:last-of-type{*/
2003/*    border-top: 1px solid #f0f0f0;*/
2004/*    border-bottom: 1px solid #f0f0f0;*/
2005/*}*/
2006.tr{
2007    background-color: #fff;
2008}
2009.tr:hover{
2010    background-color: #f3f3f3; /*antd #fafafa 42b983*/
2011}
2012.td{
2013    background-color: inherit;
2014    box-sizing: border-box;
2015    padding: 10px;
2016    display: flex;
2017    justify-content: flex-start;
2018    align-items: center;
2019    width: 100%;
2020    height: auto;
2021    /*overflow: auto;*/
2022    border-left: 1px solid #f0f0f0;
2023}
2024.td-order{
2025    /*background: green;*/
2026}
2027.td-order:before{
2028
2029}
2030.td:last-of-type{
2031    border-right: 1px solid #f0f0f0;
2032}
2033.table{
2034     color: #262626;
2035}
2036:host(:not([noheader])) .thead{
2037    display: grid;
2038    position: sticky;
2039    top: 0;
2040    font-weight: bold;
2041    font-size: .9rem;
2042    color: #fff;
2043    /*width: 100%;*/
2044    background-color: #42b983;
2045    z-index: 1;
2046}
2047/*配置有 noheader 表示不限时表头,tbody上添加 border-top*/
2048:host([noheader]) .thead{
2049    display: none;
2050    position: sticky;
2051    top: 0;
2052    font-weight: bold;
2053    font-size: .9rem;
2054    color: #fff;
2055    /*width: 100%;*/
2056    background-color: #42b983;
2057    z-index: 1;
2058}
2059:host([noheader]) .tbody{
2060    border-top: 1px solid #f0f0f0;
2061}
2062
2063.tbody{
2064    width: 100%;
2065    height: ${this.scrollY};
2066    display: grid;
2067    grid-template-columns: 1fr;
2068    row-gap: 1px;
2069    column-gap: 1px;
2070    background-color: #f0f0f0;
2071    border-bottom: 1px solid #f0f0f0;
2072    /*overflow:  auto;*/
2073    ${this.scrollY === 'auto' ? '' : 'overflow-y: auto'};
2074}
2075.th{
2076    display: grid;
2077    background-color: #42b983;
2078    /*position: sticky;*/
2079    /*top: 0;*/
2080}
2081
2082.tree-icon{
2083    font-size: 1.2rem;
2084    width: 20px;
2085    height: 20px;
2086    padding-right: 5px;
2087    padding-left: 5px;
2088    cursor: pointer;
2089}
2090.tree-icon:hover{
2091    color: #42b983;
2092}
2093.row-checkbox,row-checkbox-all{
2094
2095}
2096
2097.up-svg{
2098    position: absolute;
2099    right: 5px;
2100    top: 8px;
2101    width: 15px;
2102    height: 15px;
2103}
2104.down-svg{
2105    position: absolute;
2106    right: 5px;
2107    bottom: 8px;
2108    width: 15px;
2109    height: 15px;
2110}
2111</style>
2112
2113<slot id="slot" style="display: none"></slot>
2114<div class="table">
2115    <div class="thead"></div>
2116    <div class="tbody"></div>
2117</div>
2118        `
2119
2120            }
2121
2122            /*根据column[]嵌套结构得到 grid css布局描述*/
2123
2124            //当 custom element首次被插入文档DOM时,被调用。
2125            connectedCallback() {
2126                this.st = this.shadowRoot.querySelector('#slot');
2127                this.tableElement = this.shadowRoot.querySelector('.table');
2128                this.theadElement = this.shadowRoot.querySelector('.thead');
2129                this.tbodyElement = this.shadowRoot.querySelector('.tbody');
2130                this.tableColumns = this.querySelectorAll('lit-table-column');
2131                this.colCount = this.tableColumns.length;
2132                this.st.addEventListener('slotchange', (event) => {
2133                    this.theadElement.innerHTML = '';
2134                    setTimeout(() => {
2135                        this.columns = this.st.assignedElements();
2136
2137                        let rowElement = document.createElement('div');
2138                        rowElement.classList.add('th');
2139                        if (this.selectable) {
2140                            let box = document.createElement('div');
2141                            box.style.display = 'flex';
2142                            box.style.justifyContent = 'center';
2143                            box.style.alignItems = 'center';
2144                            box.style.gridArea = "_checkbox_";
2145                            box.classList.add('td');
2146                            box.style.backgroundColor = "#ffffff66";
2147                            let checkbox = document.createElement('lit-checkbox');
2148                            checkbox.classList.add('row-checkbox-all');
2149                            // checkbox.style.boxShadow = '0 0 1px #fff';
2150                            // console.log(checkbox.shadowRoot.querySelector('input'));
2151                            checkbox.onchange = e => {
2152                                this.shadowRoot.querySelectorAll('.row-checkbox').forEach(a => a.checked = e.detail.checked);
2153                                if (e.detail.checked) {
2154                                    this.shadowRoot.querySelectorAll('.tr').forEach(a => a.setAttribute('checked', ''));
2155                                } else {
2156                                    this.shadowRoot.querySelectorAll('.tr').forEach(a => a.removeAttribute('checked'));
2157                                }
2158                            }
2159
2160                            box.appendChild(checkbox);
2161                            rowElement.appendChild(box);
2162                        }
2163
2164                        // let getGridDesc = (columns)=>{
2165                        //     columns.forEach(a=>{
2166                        //         // console.log(a);
2167                        //         if(a.tagName==='LIT-TABLE-GROUP'){
2168                        //             let children = [...a.querySelectorAll('lit-table-column')];
2169                        //             // console.log(children);
2170                        //             getGridDesc(children)
2171                        //         }else{
2172                        //             // console.log([...a.querySelectorAll('lit-table-column')]);
2173                        //         }
2174                        //     })
2175                        // }
2176                        // getGridDesc(this.columns);
2177                        let area = [], gridTemplateColumns = [];
2178                        let resolvingArea = (columns, x, y) => {
2179                            columns.forEach((a, i) => {
2180                                // console.log(a.getAttribute('key'),i);
2181                                if (!area[y]) area[y] = []
2182                                let key = a.getAttribute('key') || a.getAttribute('title')
2183                                if (a.tagName === 'LIT-TABLE-GROUP') {
2184                                    let len = a.querySelectorAll('lit-table-column').length;
2185                                    let children = [...a.children].filter(a => a.tagName !== 'TEMPLATE');
2186                                    if (children.length > 0) {
2187                                        resolvingArea(children, x, y + 1);
2188                                    }
2189                                    for (let j = 0; j < len; j++) {
2190                                        area[y][x] = { x, y, t: key };
2191                                        x++;
2192                                    }
2193                                    let h = document.createElement('div');
2194                                    h.classList.add('td');
2195                                    h.style.justifyContent = a.getAttribute('align')
2196                                    h.style.borderBottom = '1px solid #f0f0f0'
2197                                    h.style.gridArea = key;
2198                                    h.innerText = a.title;
2199                                    if (a.hasAttribute('fixed')) {
2200                                        this.fixed(h, a.getAttribute('fixed'), "#42b983")
2201                                    }
2202                                    rowElement.append(h);
2203                                } else if (a.tagName === 'LIT-TABLE-COLUMN') {
2204                                    area[y][x] = { x, y, t: key };
2205                                    x++;
2206                                    let h = document.createElement('div');
2207                                    h.classList.add('td');
2208                                    if (a.hasAttribute('order')) {
2209                                        h.sortType = 0;
2210                                        h.classList.add('td-order');
2211                                        h.style.position = "relative"
2212                                        let NS = "http://www.w3.org/2000/svg";
2213                                        let upSvg = document.createElementNS(NS, "svg");
2214                                        let upPath = document.createElementNS(NS, "path");
2215                                        upSvg.setAttribute('fill', '#efefef');
2216                                        upSvg.setAttribute('viewBox', '0 0 1024 1024');
2217                                        upSvg.setAttribute('stroke', '#000000');
2218                                        upSvg.classList.add('up-svg');
2219                                        // upPath.setAttribute("d", "M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3c-3.8 5.3-0.1 12.7 6.5 12.7h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z");
2220                                        upPath.setAttribute("d", "M858.9 689L530.5 308.2c-9.4-10.9-27.5-10.9-37 0L165.1 689c-12.2 14.2-1.2 35 18.5 35h656.8c19.7 0 30.7-20.8 18.5-35z");
2221                                        // upPath.setAttribute("fill", "#ffffff");
2222                                        // upPath.setAttribute("stroke-width", "2px");
2223                                        // upPath.setAttribute("stroke", "rgba(207, 219, 230, 1)");
2224                                        // upPath.setAttribute("marker-end", "url(#markerArrow)");
2225                                        upSvg.appendChild(upPath);
2226                                        let downSvg = document.createElementNS(NS, "svg");
2227                                        let downPath = document.createElementNS(NS, "path");
2228                                        downSvg.setAttribute('fill', '#efefef');
2229                                        downSvg.setAttribute('viewBox', '0 0 1024 1024');
2230                                        downSvg.setAttribute('stroke', '#efefef');
2231                                        downSvg.classList.add('down-svg');
2232                                        // downPath.setAttribute("d", "M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3 0.1-12.7-6.4-12.7z");
2233                                        downPath.setAttribute("d", "M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z");
2234                                        // downPath.setAttribute("fill", "#ffffff");
2235                                        // downPath.setAttribute("stroke-width", "2px");
2236                                        // downPath.setAttribute("stroke", "rgba(207, 219, 230, 1)");
2237                                        // downPath.setAttribute("marker-end", "url(#markerArrow)");
2238                                        downSvg.appendChild(downPath)
2239                                        if (i == 0) {
2240                                            h.sortType = 2; // 默认以第一列 降序排序 作为默认排序
2241                                            upSvg.setAttribute('fill', '#efefef');
2242                                            downSvg.setAttribute('fill', '#333');
2243                                        }
2244                                        h.appendChild(upSvg);
2245                                        h.appendChild(downSvg);
2246                                        h.onclick = ev => {
2247                                            this.shadowRoot.querySelectorAll('.td-order svg').forEach(it => {
2248                                                it.setAttribute('fill', '#efefef');
2249                                                it.setAttribute('fill', '#efefef');
2250                                                it.sortType = 0;
2251                                            })
2252                                            if (h.sortType == undefined || h.sortType == null) {
2253                                                h.sortType = 0;
2254                                            } else if (h.sortType === 2) {
2255                                                h.sortType = 0;
2256                                            } else {
2257                                                h.sortType += 1;
2258                                            }
2259                                            // if(h.sortType == 2){
2260                                            //     h.sortType = 1
2261                                            // }else{
2262                                            //     h.sortType = 2
2263                                            // }
2264                                            switch (h.sortType) {
2265                                                case 1:
2266                                                    upSvg.setAttribute('fill', '#333');
2267                                                    downSvg.setAttribute('fill', '#efefef');
2268                                                    break;
2269                                                case 2:
2270                                                    upSvg.setAttribute('fill', '#efefef');
2271                                                    downSvg.setAttribute('fill', '#333');
2272                                                    break;
2273                                                default:
2274                                                    upSvg.setAttribute('fill', "#efefef");
2275                                                    downSvg.setAttribute('fill', "#efefef");
2276                                                    break;
2277                                            }
2278                                            this.dispatchEvent(new CustomEvent("ColumnClick", {
2279                                                detail: {
2280                                                    sort: h.sortType, key: key
2281                                                }, composed: true
2282                                            }))
2283                                        }
2284                                    }
2285                                    h.style.justifyContent = a.getAttribute('align')
2286                                    gridTemplateColumns.push(a.getAttribute('width') || '1fr');
2287                                    h.style.gridArea = key;
2288                                    let titleLabel = document.createElement("label");
2289                                    titleLabel.textContent = a.title;
2290                                    // h.innerText = a.title;
2291                                    h.appendChild(titleLabel);
2292                                    if (a.hasAttribute('fixed')) {
2293                                        this.fixed(h, a.getAttribute('fixed'), "#42b983")
2294                                    }
2295                                    rowElement.append(h);
2296                                }
2297
2298                                // console.log(str)
2299                                // console.log(a.title,i,a.querySelectorAll('lit-table-column').length);
2300                            })
2301                        }
2302                        resolvingArea(this.columns, 0, 0);
2303                        area.forEach((rows, j, array) => {
2304                            for (let i = 0; i < this.colCount; i++) {
2305                                if (!rows[i]) rows[i] = array[j - 1][i];
2306                            }
2307                        })
2308
2309                        // console.log(a.map(aa=>aa.t).join(' '));
2310                        // console.log(gridTemplateColumns.join(' '));
2311                        this.gridTemplateColumns = gridTemplateColumns.join(' ');
2312                        if (this.selectable) {
2313                            let s = area.map(a => '"_checkbox_ ' + (a.map(aa => aa.t).join(' ')) + '"').join(' ');
2314                            rowElement.style.gridTemplateColumns = "60px " + gridTemplateColumns.join(' ');//`repeat(${this.colCount},1fr)`
2315                            rowElement.style.gridTemplateRows = `repeat(${area.length},1fr)`
2316                            rowElement.style.gridTemplateAreas = s
2317                        } else {
2318                            let s = area.map(a => '"' + (a.map(aa => aa.t).join(' ')) + '"').join(' ');
2319                            rowElement.style.gridTemplateColumns = gridTemplateColumns.join(' ');//`repeat(${this.colCount},1fr)`
2320                            rowElement.style.gridTemplateRows = `repeat(${area.length},1fr)`
2321                            rowElement.style.gridTemplateAreas = s
2322                        }
2323                        this.theadElement.append(rowElement);
2324                        if (this.hasAttribute('tree')) {
2325                            this.renderTreeTable();
2326                        } else {
2327                            this.renderTable();
2328                        }
2329                    });
2330
2331                });
2332
2333                // this.shadowRoot.addEventListener("load", function (event) {
2334                //     console.log("DOM fully loaded and parsed");
2335                // });
2336            }
2337
2338            //当 custom element从文档DOM中删除时,被调用。
2339            disconnectedCallback() {
2340
2341            }
2342
2343            //当 custom element被移动到新的文档时,被调用。
2344            adoptedCallback() {
2345                console.log('Custom square element moved to new page.');
2346            }
2347
2348            //当 custom element增加、删除、修改自身属性时,被调用。
2349            attributeChangedCallback(name, oldValue, newValue) {
2350
2351            }
2352
2353            fixed(td, placement, bgColor, zIndex) {
2354                td.style.position = 'sticky';
2355                if (placement === "left") {
2356                    td.style.left = '0px';
2357                    // td.style.borderRight = '1px solid #f0f0f0';
2358                    td.style.boxShadow = '3px 0px 5px #33333333'
2359                    // td.style.backgroundColor=bgColor
2360                } else if (placement === "right") {
2361                    td.style.right = '0px';
2362                    td.style.boxShadow = '-3px 0px 5px #33333333'
2363                    // td.style.borderLeft = '1px solid #f0f0f0';
2364                    // td.style.backgroundColor=bgColor
2365                }
2366            }
2367
2368            /*渲染成表格*/
2369            renderTable() {
2370                let that = this;
2371                if (!this.columns) return;
2372                if (!this.ds) return; // 如果没有设置数据源,直接返回
2373                this.tbodyElement.innerHTML = '';//清空表格内容
2374                // this.style.gridTemplateRows = `repeat(${this.ds.length}*2 ,1fr)`;
2375                this.ds.forEach(rowData => {
2376                    let rowElement = document.createElement('div');
2377                    rowElement.classList.add('tr');
2378                    rowElement.data = rowData;
2379                    let gridTemplateColumns = []
2380                    //如果table配置了selectable(选择行模式) 单独在行头部添加一个 checkbox
2381                    if (this.selectable) {
2382                        let box = document.createElement('div');
2383                        box.style.display = 'flex';
2384                        box.style.justifyContent = 'center';
2385                        box.style.alignItems = 'center';
2386                        box.classList.add('td');
2387                        let checkbox = document.createElement('lit-checkbox');
2388                        checkbox.classList.add('row-checkbox');
2389                        checkbox.onchange = (e) => {//checkbox 的是否选中 会影响  行对应的 div上是否有 checked属性,用于标记
2390                            if (e.detail.checked) {
2391                                rowElement.setAttribute('checked', "");
2392                            } else {
2393                                rowElement.removeAttribute('checked');
2394                            }
2395                        }
2396                        box.appendChild(checkbox);
2397                        rowElement.appendChild(box);
2398                    }
2399                    this.tableColumns.forEach(cl => {
2400                        let dataIndex = cl.getAttribute('data-index');
2401                        gridTemplateColumns.push(cl.getAttribute('width') || '1fr')
2402                        if (cl.template) {//如果自定义渲染,从模版中渲染得到节点
2403                            let cloneNode = cl.template.render(rowData).content.cloneNode(true);
2404                            // cloneNode.classList.add('td');
2405                            let d = document.createElement('div');
2406                            d.classList.add('td');
2407                            d.style.justifyContent = cl.getAttribute('align')
2408                            if (cl.hasAttribute('fixed')) {
2409                                this.fixed(d, cl.getAttribute('fixed'), "#ffffff")
2410                            }
2411                            d.append(cloneNode);
2412                            rowElement.append(d);
2413                        } else {
2414                            let td = document.createElement('div');
2415                            td.classList.add('td');
2416                            td.style.justifyContent = cl.getAttribute('align')
2417                            if (cl.hasAttribute('fixed')) {
2418                                this.fixed(td, cl.getAttribute('fixed'), "#ffffff")
2419                            }
2420                            // td.style.position='sticky';
2421                            // td.innerHTML = rowData[dataIndex];
2422                            td.innerHTML = `<code style="padding:0;margin:0">${rowData[dataIndex].toString().replace('\n', "")}</code>`;
2423                            // console.log(cl,cl.template);
2424                            rowElement.append(td);
2425                        }
2426
2427                    })
2428                    if (this.selectable) { //如果 带选择的table 前面添加一个 60px的列
2429                        rowElement.style.gridTemplateColumns = '60px ' + gridTemplateColumns.join(' ');//`repeat(${this.colCount},1fr)`
2430                    } else {
2431                        rowElement.style.gridTemplateColumns = gridTemplateColumns.join(' ');//`repeat(${this.colCount},1fr)`
2432                    }
2433                    rowElement.onclick = e => {
2434                        this.dispatchEvent(new CustomEvent('onRowClick', { detail: rowData, composed: true }));
2435                    }
2436                    this.tbodyElement.append(rowElement);
2437                })
2438            }
2439
2440            /*渲染树表结构*/
2441            renderTreeTable() {
2442                if (!this.columns) return;
2443                if (!this.ds) return;
2444                this.tbodyElement.innerHTML = '';
2445                /*通过list 构建 tree 结构*/
2446                let ids = JSON.parse(this.getAttribute('tree') || `["id","pid"]`);
2447                let toTreeData = (data, id, pid) => {
2448                    let cloneData = JSON.parse(JSON.stringify(data));
2449                    return cloneData.filter(father => {
2450                        let branchArr = cloneData.filter(child => father[id] == child[pid]);
2451                        branchArr.length > 0 ? father['children'] = branchArr : '';
2452                        return !father[pid];
2453                    });
2454                }
2455                let treeData = toTreeData(this.ds, ids[0], ids[1]);//
2456                // console.log(treeData);
2457                let offset = 30;
2458                let offsetVal = offset;
2459                const drawRow = (arr, parentNode) => {
2460                    arr.forEach(rowData => {
2461                        let rowElement = document.createElement('div');
2462                        rowElement.classList.add('tr');
2463                        rowElement.data = rowData;
2464                        let gridTemplateColumns = [];
2465                        if (this.selectable) {
2466                            let box = document.createElement('div');
2467                            box.style.display = 'flex';
2468                            box.style.justifyContent = 'center';
2469                            box.style.alignItems = 'center';
2470                            box.classList.add('td');
2471                            let checkbox = document.createElement('lit-checkbox');
2472                            checkbox.classList.add('row-checkbox');
2473                            checkbox.onchange = (e) => {
2474                                if (e.detail.checked) {
2475                                    rowElement.setAttribute('checked', "");
2476                                } else {
2477                                    rowElement.removeAttribute('checked');
2478                                }
2479                                const changeChildNode = (rowElement, checked) => {
2480                                    let id = rowElement.getAttribute('id');
2481                                    let pid = rowElement.getAttribute('pid');
2482                                    this.shadowRoot.querySelectorAll(`div[pid=${id}]`).forEach(a => {
2483                                        a.querySelector('.row-checkbox').checked = checked;
2484                                        if (checked) {
2485                                            a.setAttribute('checked', '');
2486                                        } else {
2487                                            a.removeAttribute('checked');
2488                                        }
2489                                        changeChildNode(a, checked);
2490                                    });
2491                                };
2492                                changeChildNode(rowElement, e.detail.checked);
2493                            }
2494                            box.appendChild(checkbox);
2495                            rowElement.appendChild(box);
2496                        }
2497                        this.tableColumns.forEach((cl, index) => {
2498                            let dataIndex = cl.getAttribute('data-index');
2499                            gridTemplateColumns.push(cl.getAttribute('width') || '1fr')
2500                            let td;
2501                            if (cl.template) {
2502                                let cloneNode = cl.template.render(rowData).content.cloneNode(true);
2503                                // cloneNode.classList.add('td');
2504                                td = document.createElement('div');
2505                                td.classList.add('td');
2506                                td.style.justifyContent = cl.getAttribute('align')
2507                                if (cl.hasAttribute('fixed')) {
2508                                    this.fixed(td, cl.getAttribute('fixed'), "#ffffff")
2509                                }
2510                                td.append(cloneNode);
2511                            } else {
2512                                td = document.createElement('div');
2513                                td.classList.add('td');
2514                                td.style.justifyContent = cl.getAttribute('align')
2515                                if (cl.hasAttribute('fixed')) {
2516                                    this.fixed(td, cl.getAttribute('fixed'), "#ffffff")
2517                                }
2518                                // td.style.position='sticky';
2519                                td.innerHTML = rowData[dataIndex];
2520                                // console.log(cl,cl.template);
2521                            }
2522                            if (index === 0) {
2523                                if (rowData.children && rowData.children.length > 0) {
2524                                    let btn = document.createElement('lit-icon');
2525                                    btn.classList.add('tree-icon');
2526                                    btn.name = 'minus-square';
2527                                    td.insertBefore(btn, td.firstChild);
2528                                    td.style.paddingLeft = (offsetVal - 30) + 'px';
2529                                    btn.onclick = (e) => {
2530                                        const foldNode = (rowElement) => {
2531                                            let id = rowElement.getAttribute('id');
2532                                            let pid = rowElement.getAttribute('pid');
2533                                            this.shadowRoot.querySelectorAll(`div[pid=${id}]`).forEach(a => {
2534                                                let id = a.getAttribute('id');
2535                                                let pid = a.getAttribute('pid');
2536                                                a.style.display = 'none';
2537                                                foldNode(a);
2538                                            });
2539                                            if (rowElement.querySelector('.tree-icon')) {
2540                                                rowElement.querySelector('.tree-icon').name = 'plus-square';
2541                                            }
2542                                            rowElement.removeAttribute('expand');
2543                                        };
2544                                        const expendNode = (rowElement) => {
2545                                            let id = rowElement.getAttribute('id');
2546                                            let pid = rowElement.getAttribute('pid');
2547                                            this.shadowRoot.querySelectorAll(`div[pid=${id}]`).forEach(a => {
2548                                                let id = a.getAttribute('id');
2549                                                let pid = a.getAttribute('pid');
2550                                                a.style.display = '';
2551                                                // expendNode(a);
2552                                            });
2553                                            if (rowElement.querySelector('.tree-icon')) {
2554                                                rowElement.querySelector('.tree-icon').name = 'minus-square';
2555                                            }
2556                                            rowElement.setAttribute('expand', '');
2557                                        }
2558                                        if (rowElement.hasAttribute('expand')) {
2559                                            foldNode(rowElement);
2560                                        } else {
2561                                            expendNode(rowElement);
2562                                        }
2563                                    };
2564                                } else {
2565                                    td.style.paddingLeft = offsetVal + 'px';
2566                                }
2567                            }
2568                            rowElement.append(td);
2569
2570                        })
2571                        if (this.selectable) {
2572                            rowElement.style.gridTemplateColumns = '60px ' + gridTemplateColumns.join(' ');//`repeat(${this.colCount},1fr)`
2573                        } else {
2574                            rowElement.style.gridTemplateColumns = gridTemplateColumns.join(' ');//`repeat(${this.colCount},1fr)`
2575                        }
2576                        rowElement.onclick = e => {
2577                            // console.log(rowElement.style.gridTemplateColumns);
2578                            // LitMessage.info(JSON.stringify(rowData));
2579                        }
2580                        parentNode.append(rowElement);
2581                        rowElement.setAttribute('id', rowData[ids[0]]);
2582                        rowElement.setAttribute('pid', rowData[ids[1]]);
2583                        rowElement.setAttribute('expand', '');
2584                        if (rowData.children && rowData.children.length > 0) {
2585                            //有子节点的 前面加上 + 图标 表示可以展开节点
2586
2587                            offsetVal = offsetVal + offset;
2588                            drawRow(rowData.children, parentNode);
2589                            offsetVal = offsetVal - offset;
2590                        }
2591                    });
2592                };
2593                drawRow(treeData, this.tbodyElement);
2594            }
2595
2596            //获取选中的行数据
2597            getCheckRows() {
2598                return [...this.shadowRoot.querySelectorAll('div[class=tr][checked]')].map(a => a.data).map(a => {
2599                    delete a['children'];
2600                    return a;
2601                });
2602            }
2603
2604            deleteRowsCondition(fn) {
2605                this.shadowRoot.querySelectorAll("div[class=tr]").forEach(tr => {
2606                    if (fn(tr.data)) {
2607                        tr.remove();
2608                    }
2609                })
2610            }
2611        }
2612        if (!customElements.get('lit-table')) {
2613            customElements.define('lit-table', LitTable);
2614        }
2615
2616        class LitTableColumn extends HTMLElement {
2617            static get observedAttributes() {
2618                return ['name', 'order']
2619            }
2620
2621            constructor() {
2622                super();
2623                const shadowRoot = this.attachShadow({ mode: 'open' });
2624                shadowRoot.innerHTML = `
2625<style>
2626:host{ }
2627</style>
2628<slot id="slot"></slot>
2629        `
2630            }
2631            //当 custom element首次被插入文档DOM时,被调用。
2632            connectedCallback() {
2633                this.template = null;
2634                this.st = this.shadowRoot.querySelector('#slot')
2635                this.st.addEventListener('slotchange', () => {
2636                    const elements = this.st.assignedElements({ flatten: false });
2637                    if (elements.length > 0) {
2638                        this.template = elements[0];
2639                    }
2640                })
2641            }
2642
2643            //当 custom element从文档DOM中删除时,被调用。
2644            disconnectedCallback() {
2645
2646            }
2647
2648            //当 custom element被移动到新的文档时,被调用。
2649            adoptedCallback() {
2650                console.log('Custom square element moved to new page.');
2651            }
2652
2653            //当 custom element增加、删除、修改自身属性时,被调用。
2654            attributeChangedCallback(name, oldValue, newValue) {
2655
2656            }
2657        }
2658        if (!customElements.get('lit-table-column')) {
2659            customElements.define('lit-table-column', LitTableColumn);
2660        }
2661
2662        class LitTableGroup extends HTMLElement {
2663            static get observedAttributes() {
2664                return ['title']
2665            }
2666
2667            get title() {
2668                return this.getAttribute('title');
2669            }
2670
2671            set title(value) {
2672                this.setAttribute('title', value);
2673            }
2674
2675            constructor() {
2676                super();
2677                const shadowRoot = this.attachShadow({ mode: 'open' });
2678                shadowRoot.innerHTML = `
2679        <style>
2680        :host{ }
2681        </style>
2682        <slot id="sl"></slot>
2683        `
2684            }
2685
2686            //当 custom element首次被插入文档DOM时,被调用。
2687            connectedCallback() {
2688
2689            }
2690
2691            //当 custom element从文档DOM中删除时,被调用。
2692            disconnectedCallback() {
2693
2694            }
2695
2696            //当 custom element被移动到新的文档时,被调用。
2697            adoptedCallback() {
2698                console.log('Custom square element moved to new page.');
2699            }
2700
2701            //当 custom element增加、删除、修改自身属性时,被调用。
2702            attributeChangedCallback(name, oldValue, newValue) {
2703
2704            }
2705        }
2706        if (!customElements.get('lit-table-group')) {
2707            customElements.define('lit-table-group', LitTableGroup);
2708        }
2709
2710        class LitTabpane extends HTMLElement {
2711            static get observedAttributes() { return ['tab', 'key', 'disabled', 'icon', 'closeable', 'hide']; }
2712            constructor() {
2713                super();
2714                const shadowRoot = this.attachShadow({ mode: 'open' });
2715                shadowRoot.innerHTML = `
2716<style>
2717:host(){
2718    scroll-behavior: smooth;
2719    -webkit-overflow-scrolling: touch;
2720    overflow: auto;
2721    width: 100%;
2722}
2723</style>
2724<slot></slot>
2725`
2726            }
2727
2728            get tab() {
2729                return this.getAttribute('tab');
2730            }
2731
2732            set tab(value) {
2733                this.setAttribute("tab", value);
2734            }
2735
2736            get icon() {
2737                return this.getAttribute("icon");
2738            }
2739
2740            get disabled() {
2741                return this.getAttribute('disabled') !== null;
2742            }
2743
2744            set disabled(value) {
2745                if (value === null || value === false) {
2746                    this.removeAttribute("disabled");
2747                } else {
2748                    this.setAttribute("disabled", value);
2749                }
2750            }
2751            get closeable() {
2752                return this.getAttribute('closeable') !== null;
2753            }
2754            set closeable(value) {
2755                if (value === null || value === false) {
2756                    this.removeAttribute("closeable");
2757                } else {
2758                    this.setAttribute("closeable", value);
2759                }
2760            }
2761
2762            get key() {
2763                return this.getAttribute("key");
2764            }
2765            set key(value) {
2766                this.setAttribute("key", value);
2767            }
2768
2769            get hide() {
2770                return this.getAttribute('hide') !== null;
2771            }
2772            set hide(value) {
2773                if (value === null || value === false) {
2774                    this.removeAttribute("hide");
2775                } else {
2776                    this.setAttribute("hide", value);
2777                }
2778            }
2779
2780            //当 custom element首次被插入文档DOM时,被调用。
2781            connectedCallback() { }
2782
2783            //当 custom element从文档DOM中删除时,被调用。
2784            disconnectedCallback() { }
2785
2786            //当 custom element被移动到新的文档时,被调用。
2787            adoptedCallback() { }
2788
2789            //当 custom element增加、删除、修改自身属性时,被调用。
2790            attributeChangedCallback(name, oldValue, newValue) {
2791                if (oldValue !== newValue && newValue !== undefined) {
2792                    if (name === 'tab' && this.parentNode) {
2793                        this.parentNode.updateLabel && this.parentNode.updateLabel(this.key, newValue);
2794                    }
2795                    if (name === 'disabled' && this.parentNode) {
2796                        this.parentNode.updateDisabled && this.parentNode.updateDisabled(this.key, newValue);
2797                    }
2798                    if (name === 'closeable' && this.parentNode) {
2799                        this.parentNode.updateCloseable && this.parentNode.updateCloseable(this.key, newValue);
2800                    }
2801                    if (name === 'hide' && this.parentNode) {
2802                        this.parentNode.updateCloseable && this.parentNode.updateHide(this.key, newValue);
2803                    }
2804                }
2805            }
2806        }
2807        if (!customElements.get('lit-tabpane')) {
2808            customElements.define('lit-tabpane', LitTabpane);
2809        }
2810
2811        class LitTabs extends HTMLElement {
2812            static get observedAttributes() {
2813                //mode = flat(default) | card
2814                //position = top | top-left | top-center | top-right | left | left-top | left-center | left-bottom | right | bottom
2815                return ['activekey', 'mode', 'position']
2816            }
2817
2818            constructor() {
2819                super();
2820                const shadowRoot = this.attachShadow({ mode: 'open' });
2821                shadowRoot.innerHTML = `
2822        <style>
2823        :host{
2824            display: block;
2825            text-align: unset;
2826            color: #252525;
2827            background-color: #fff;
2828            box-shadow: #00000033 0 0 10px ;
2829            /*padding: 10px;*/
2830            /*margin-right: 10px;*/
2831        }
2832        ::slotted(lit-tabpane){
2833            box-sizing:border-box;
2834            width:100%;
2835            height:100%;
2836            /*padding:10px;*/
2837            flex-shrink:0;
2838            overflow:auto;
2839        }
2840        .nav-item{
2841            display: inline-flex;
2842            justify-content: center;
2843            align-items: center;
2844            padding: 6px 0px 6px 12px;
2845            font-size: .9rem;
2846            font-weight: normal;
2847            cursor: pointer;
2848            transition: all 0.3s;
2849            flex-shrink: 0;
2850        }
2851        .nav-item lit-icon{
2852            margin-right: 2px;
2853            font-size: inherit;
2854        }
2855        .nav-item:hover{
2856            color: #42b983;
2857        }
2858        .nav-item[data-disabled]{
2859            pointer-events: all;
2860            cursor: not-allowed;
2861            color: #bfbfbf;
2862        }
2863        .nav-item[data-selected]{
2864            color: #42b983;;
2865        }
2866        .tab-content{
2867            display: block;
2868            background-color: #fff;
2869            flex:1;
2870        }
2871
2872        /*
2873         *   top  top-left top-center top-right
2874         */
2875        :host(:not([position])) .nav-root,
2876        :host([position^='top']) .nav-root{
2877            display: flex;
2878            position: relative;
2879            justify-content: center;
2880            align-items: center;
2881        }
2882        :host(:not([mode]):not([position])) .tab-line,/*移动的线条*/
2883        :host([mode='flat'][position^='top']) .tab-line{
2884            position:absolute;
2885            bottom: 2px;
2886            background-color: #42b983;
2887            height: 2px;
2888            transform: translateY(100%);
2889            transition: all 0.3s;
2890        }
2891
2892        :host(:not([position])) .tab-nav-container,
2893        :host([position^='top']) .tab-nav-container{
2894            display: flex;
2895            position: relative;
2896            flex-direction: column;
2897            overflow-y: hidden;
2898            overflow-x: auto;
2899            overflow: -moz-scrollbars-none;
2900            -ms-overflow-style: none;
2901            transition: all 0.3s;
2902            flex: 1;
2903            /*background: linear-gradient(90deg,white 30%, transparent),radial-gradient(at 0 50%, rgba(0,0,0,.2),transparent 70%);*/
2904            /*background-repeat: no-repeat;*/
2905            /*background-size: 50px 100%, 15px 100%;*/
2906            /*background-attachment: local,scroll,local,scroll;*/
2907            /*border-bottom: #f0f0f0 1px solid;*/
2908        }
2909        :host([position='top']) .tab-nav,
2910        :host([position='top-left']) .tab-nav{
2911            display: flex;
2912            position: relative;
2913            justify-content: flex-start;
2914        }
2915        :host([position='top-center']) .tab-nav{
2916            display: flex;
2917            justify-content: center;
2918        }
2919        :host([position='top-right']) .tab-nav{
2920            display: flex;
2921            justify-content: flex-end;
2922        }
2923
2924        :host([position^='top'][mode='card']) .nav-item{
2925            border-top: 1px solid #f0f0f0;
2926            border-left: 1px solid #f0f0f0;
2927            border-right: 1px solid #f0f0f0;
2928            border-bottom: 1px solid #f0f0f0;
2929            bottom: 0px;
2930            margin-right: 2px;
2931            position: relative;
2932        }
2933        :host([position^='top']) .tab-nav-bg-line{
2934            position: absolute;bottom: 0;height: 1px;background-color: #f0f0f0;width: 100%
2935        }
2936        :host([position^='top'][mode='card']) .nav-item:not([data-selected]){
2937            background-color: #f6f6f6;
2938            border-bottom: 1px solid #f0f0f0;
2939        }
2940        :host([position^='top'][mode='card']) .nav-item[data-selected]{
2941            background-color: #ffffff;
2942            border-bottom: 1px solid #fff;
2943            bottom: 0px;
2944        }
2945        /*
2946            bottom bottom-left bottom-center bottom-right
2947        */
2948        :host([position^='bottom']) .tab{
2949            display: flex;
2950            flex-direction: column-reverse;
2951        }
2952        :host([mode='flat'][position^='bottom']) .tab-line{
2953            position:absolute;
2954            top: -3px;
2955            background-color: #42b983;
2956            height: 2px;
2957            transform: translateY(-100%);
2958            transition: all 0.3s;
2959        }
2960        :host([position^='bottom']) .tab-nav-container{
2961            display: flex;
2962            position: relative;
2963            flex-direction: column;
2964            overflow-x: auto;
2965            overflow-y: visible;
2966            overflow: -moz-scrollbars-none;
2967            -ms-overflow-style: none;
2968            transition: all 0.3s;
2969            flex: 1;
2970            /*background: linear-gradient(90deg,white 30%, transparent),radial-gradient(at 0 50%, rgba(0,0,0,.2),transparent 70%);*/
2971            /*background-repeat: no-repeat;*/
2972            /*background-size: 50px 100%, 15px 100%;*/
2973            /*background-attachment: local,scroll,local,scroll;*/
2974            border-top: #f0f0f0 1px solid;
2975        }
2976        :host([position^='bottom']) .nav-root{
2977            display: flex;
2978            justify-content: center;
2979            align-items: center;
2980        }
2981        :host([position='bottom']) .tab-nav,
2982        :host([position='bottom-left']) .tab-nav{
2983            display: flex;
2984            position: relative;
2985            justify-content: flex-start;
2986        }
2987        :host([position='bottom-center']) .tab-nav{
2988            display: flex;
2989            justify-content: center;
2990        }
2991        :host([position='bottom-right']) .tab-nav{
2992            display: flex;
2993            justify-content: flex-end;
2994        }
2995        :host([position^='bottom'][mode='card']) .nav-item{
2996            border-top: 1px solid #ffffff;
2997            border-left: 1px solid #f0f0f0;
2998            border-right: 1px solid #f0f0f0;
2999            border-bottom: 1px solid #f0f0f0;
3000            top: -1px;
3001            margin-right: 2px;
3002            position: relative;
3003        }
3004        :host([position^='bottom']) .tab-nav-bg-line{
3005            position: absolute;top: 0;height: 1px;background-color: #f0f0f0;width: 100%
3006        }
3007        :host([position^='bottom'][mode='card']) .nav-item:not([data-selected]){
3008            background-color: #f5f5f5;
3009            border-top: 1px solid #f0f0f0;
3010        }
3011        :host([position^='bottom'][mode='card']) .nav-item[data-selected]{
3012            background-color: #ffffff;
3013            border-top: 1px solid #fff;
3014            top: -1px;
3015        }
3016        /*
3017        left left-top left-center left-bottom
3018        */
3019        :host([position^='left']) .tab{
3020            display: flex;
3021            flex-direction: row;
3022        }
3023        :host([mode='flat'][position^='left']) .tab-line{
3024            position:absolute;
3025            right: 1px;
3026            background-color: #42b983;
3027            width: 3px;
3028            transform: translateX(100%);
3029            transition: all 0.3s;
3030        }
3031        :host([position^='left']) .tab-nav-container{
3032            display: flex;
3033            position: relative;
3034            flex-direction: row;
3035            overflow-x: auto;
3036            overflow-y: visible;
3037            overflow: -moz-scrollbars-none;
3038            -ms-overflow-style: none;
3039            transition: all 0.3s;
3040            flex: 1;
3041            /*background: linear-gradient(90deg,white 30%, transparent),radial-gradient(at 0 50%, rgba(0,0,0,.2),transparent 70%);*/
3042            /*background-repeat: no-repeat;*/
3043            /*background-size: 50px 100%, 15px 100%;*/
3044            /*background-attachment: local,scroll,local,scroll;*/
3045            border-right: #f0f0f0 1px solid;
3046        }
3047        :host([position^='left']) .nav-root{
3048            display: flex;
3049            flex-direction: column;
3050            justify-content: center;
3051            align-items: center;
3052        }
3053        :host([position='left']) .tab-nav,
3054        :host([position='left-top']) .tab-nav{
3055            display: flex;
3056            position: relative;
3057            flex-direction: column;
3058            justify-content: flex-start;
3059        }
3060        :host([position='left-center']) .tab-nav{
3061            display: flex;
3062            position: relative;
3063            flex-direction: column;
3064            justify-content: center;
3065        }
3066        :host([position='left-bottom']) .tab-nav{
3067            display: flex;
3068            position: relative;
3069            flex-direction: column;
3070            justify-content: flex-end;
3071        }
3072        :host([position^='left'][mode='card']) .nav-item{
3073            border-top: 1px solid #f0f0f0;
3074            border-left: 1px solid #f0f0f0;
3075            border-right: 1px solid #ffffff;
3076            border-bottom: 1px solid #f0f0f0;
3077            right: -1px;
3078            margin-bottom: 2px;
3079            position: relative;
3080        }
3081        :host([position^='left']) .tab-nav-bg-line{
3082            position: absolute;right: 0;width: 1px;background-color: #f0f0f0;width: 100%
3083        }
3084        :host([position^='left'][mode='card']) .nav-item:not([data-selected]){
3085            background-color: #f5f5f5;
3086            border-right: 1px solid #f0f0f0;
3087        }
3088        :host([position^='left'][mode='card']) .nav-item[data-selected]{
3089            background-color: #ffffff;
3090            border-bottom: 1px solid #fff;
3091            right: -1px;
3092        }
3093        /*
3094        right right-top right-center right-bottom
3095        */
3096        :host([position^='right']) .tab{
3097            display: flex;
3098            flex-direction: row-reverse;
3099        }
3100        :host([mode='flat'][position^='right']) .tab-line{
3101            position:absolute;
3102            left: 1px;
3103            background-color: #42b983;
3104            width: 3px;
3105            transform: translateX(-100%);
3106            transition: all 0.3s;
3107        }
3108        :host([position^='right']) .tab-nav-container{
3109            display: flex;
3110            position: relative;
3111            flex-direction: row-reverse;
3112            overflow-x: auto;
3113            overflow-y: visible;
3114            overflow: -moz-scrollbars-none;
3115            -ms-overflow-style: none;
3116            transition: all 0.3s;
3117            flex: 1;
3118            /*background: linear-gradient(90deg,white 30%, transparent),radial-gradient(at 0 50%, rgba(0,0,0,.2),transparent 70%);*/
3119            /*background-repeat: no-repeat;*/
3120            /*background-size: 50px 100%, 15px 100%;*/
3121            /*background-attachment: local,scroll,local,scroll;*/
3122            border-left: #f0f0f0 1px solid;
3123        }
3124        :host([position^='right']) .nav-root{
3125            display: flex;
3126            flex-direction: column;
3127            justify-content: center;
3128            align-items: center;
3129        }
3130        :host([position='right']) .tab-nav,
3131        :host([position='right-top']) .tab-nav{
3132            display: flex;
3133            position: relative;
3134            flex-direction: column;
3135            justify-content: flex-start;
3136        }
3137        :host([position='right-center']) .tab-nav{
3138            display: flex;
3139            position: relative;
3140            flex-direction: column;
3141            justify-content: center;
3142        }
3143        :host([position='right-bottom']) .tab-nav{
3144            display: flex;
3145            position: relative;
3146            flex-direction: column;
3147            justify-content: flex-end;
3148        }
3149        :host([position^='right'][mode='card']) .nav-item{
3150            border-top: 1px solid #f0f0f0;
3151            border-left: 1px solid #ffffff;
3152            border-right: 1px solid #f0f0f0;
3153            border-bottom: 1px solid #f0f0f0;
3154            left: -1px;
3155            margin-top: 2px;
3156            position: relative;
3157        }
3158        :host([position^='right']) .tab-nav-bg-line{
3159            position: absolute;left: 0;width: 1px;background-color: #f0f0f0;width: 100%
3160        }
3161        :host([position^='right'][mode='card']) .nav-item:not([data-selected]){
3162            background-color: #f5f5f5;
3163            border-left: 1px solid #f0f0f0;
3164        }
3165        :host([position^='right'][mode='card']) .nav-item[data-selected]{
3166            background-color: #ffffff;
3167            left: -1px;
3168        }
3169
3170
3171        .tab-nav-container::-webkit-scrollbar {
3172            display: none;
3173        }
3174
3175
3176        /*关闭的图标*/
3177        .close-icon:hover{
3178            color: #000;
3179        }
3180        .nav-item[data-closeable] .close-icon{
3181            display: block;
3182            padding: 5px 5px 5px 5px;
3183            color: #999;
3184        }
3185        .nav-item[data-closeable] .no-close-icon{
3186            display: none;
3187        }
3188        .nav-item:not([data-closeable]) .no-close-icon{
3189            display: block;
3190        }
3191        .nav-item:not([data-closeable]) .close-icon{
3192            display: none;
3193        }
3194        .nav-item:not([data-hide]){
3195            display: block;
3196        }
3197        .nav-item[data-hide]{
3198            display: none;
3199        }
3200
3201        </style>
3202        <style id="filter"></style>
3203        <div class="tab">
3204            <div class="nav-root">
3205                <slot name="left" style="flex:1"></slot>
3206                <div class="tab-nav-container" >
3207                    <div class="tab-nav-bg-line"></div>
3208                    <div class="tab-nav" id="nav"></div>
3209                    <div class="tab-line" id="tab-line"></div>
3210                </div>
3211                <slot name="right" style="flex:1"></slot>
3212            </div>
3213            <div class="tab-content">
3214                <slot id="slot">NEED CONTENT</slot>
3215            </div>
3216        </div>
3217        `
3218            }
3219
3220            get position() {
3221                return this.getAttribute('position') || 'top';
3222            }
3223
3224            set position(value) {
3225                this.setAttribute('position', value);
3226            }
3227
3228            get mode() {
3229                return this.getAttribute('mode') || 'flat';
3230            }
3231
3232            set mode(value) {
3233                this.setAttribute('mode', value);
3234            }
3235
3236            get activekey() {
3237                return this.getAttribute("activekey");
3238            }
3239
3240            set activekey(value) {
3241                this.setAttribute('activekey', value);
3242            }
3243
3244            updateLabel(key, value) {
3245                // console.log(key, value);
3246                if (this.nav) {
3247                    let item = this.nav.querySelector(`.nav-item[data-key='${key}']`);
3248                    if (item) {
3249                        item.querySelector("span").innerHTML = value;
3250                        this.initTabPos()
3251                    }
3252                }
3253            }
3254
3255            updateDisabled(key, value) {
3256                // console.log(key, value);
3257                if (this.nav) {
3258                    let item = this.nav.querySelector(`.nav-item[data-key='${key}']`);
3259                    if (item) {
3260                        if (value) {
3261                            item.setAttribute('data-disabled', '')
3262                        } else {
3263                            item.removeAttribute('data-disabled');
3264                        }
3265                        this.initTabPos()
3266                    }
3267                }
3268            }
3269            updateCloseable(key, value) {
3270                if (this.nav) {
3271                    let item = this.nav.querySelector(`.nav-item[data-key='${key}']`);
3272                    if (item) {
3273                        if (value) {
3274                            item.setAttribute('data-closeable', '')
3275                        } else {
3276                            item.removeAttribute('data-closeable');
3277                        }
3278                        this.initTabPos()
3279                    }
3280                }
3281            }
3282            updateHide(key, value) {
3283                if (this.nav) {
3284                    let item = this.nav.querySelector(`.nav-item[data-key='${key}']`);
3285                    if (item) {
3286                        if (value) {
3287                            item.setAttribute('data-hide', '')
3288                        } else {
3289                            item.removeAttribute('data-hide');
3290                        }
3291                        this.initTabPos()
3292                    }
3293                }
3294            }
3295
3296            initTabPos() {
3297                const items = this.nav.querySelectorAll(".nav-item");
3298                Array.from(items).forEach((a, index) => {
3299                    this.tabPos[a.dataset.key] = {
3300                        index: index,
3301                        width: a.offsetWidth,
3302                        height: a.offsetHeight,
3303                        left: a.offsetLeft,
3304                        top: a.offsetTop,
3305                        label: a.textContent
3306                    }
3307                })
3308                if (this.activekey) {
3309                    if (this.position.startsWith('left')) {
3310                        this.line.style = `height:${this.tabPos[this.activekey].height}px;transform:translate(100%,${this.tabPos[this.activekey].top}px)`;
3311                    } else if (this.position.startsWith('top')) {
3312                        if (this.tabPos[this.activekey]) {
3313                            this.line.style = `width:${this.tabPos[this.activekey].width}px;transform:translate(${this.tabPos[this.activekey].left}px,100%)`;
3314                        }
3315                    } else if (this.position.startsWith('right')) {
3316                        this.line.style = `height:${this.tabPos[this.activekey].height}px;transform:translate(-100%,${this.tabPos[this.activekey].top}px)`;
3317                    } else if (this.position.startsWith('bottom')) {
3318                        this.line.style = `width:${this.tabPos[this.activekey].width}px;transform:translate(${this.tabPos[this.activekey].left}px,100%)`;
3319                    }
3320                }
3321            }
3322
3323            //当 custom element首次被插入文档DOM时,被调用。
3324            connectedCallback() {
3325                let that = this;
3326                this.tabPos = {}
3327                this.nav = this.shadowRoot.querySelector('#nav')
3328                this.line = this.shadowRoot.querySelector('#tab-line')
3329                this.slots = this.shadowRoot.querySelector('#slot');
3330                this.slots.addEventListener('slotchange', () => {
3331                    const elements = this.slots.assignedElements();
3332                    let panes = this.querySelectorAll('lit-tabpane');
3333                    if (this.activekey) {
3334                        panes.forEach(a => {
3335                            if (a.key === this.activekey) {
3336                                a.style.display = 'block'
3337                            } else {
3338                                a.style.display = 'none';
3339                            }
3340                        })
3341                    } else {
3342                        panes.forEach((a, index) => {
3343                            if (index === 0) {
3344                                a.style.display = 'block'
3345                                this.activekey = a.key
3346                            } else {
3347                                a.style.display = 'none';
3348                            }
3349                        })
3350                    }
3351
3352                    let navHtml = "";
3353                    elements.forEach(a => {
3354                        if (a.disabled) {
3355                            navHtml += `<div class="nav-item" data-key="${a.key}" data-disabled ${a.closeable ? 'data-closeable' : ''} ${a.hide ? 'data-hide' : ''}>
3356                    ${a.icon ? `<lit-icon name='${a.icon}'></lit-icon>` : ``}
3357                    <span>${a.tab}</span>
3358                    <lit-icon class="close-icon" name='close' size="12"></lit-icon><div class="no-close-icon" style="margin-right: 12px"></div>
3359                    </div>`;
3360                        } else {
3361                            if (a.key === this.activekey) {
3362                                navHtml += `<div class="nav-item" data-key="${a.key}" data-selected ${a.closeable ? 'data-closeable' : ''} ${a.hide ? 'data-hide' : ''}>
3363                        ${a.icon ? `<lit-icon name='${a.icon}'></lit-icon>` : ``}
3364                        <span>${a.tab}</span>
3365                        <lit-icon class="close-icon" name='close' size="12"></lit-icon><div class="no-close-icon" style="margin-right: 12px"></div>
3366                        </div>`;
3367                            } else {
3368                                navHtml += `<div class="nav-item" data-key="${a.key}" ${a.closeable ? 'data-closeable' : ''} ${a.hide ? 'data-hide' : ''}>
3369                            ${a.icon ? `<lit-icon name='${a.icon}'></lit-icon>` : ``}
3370                            <span>${a.tab}</span>
3371                            <lit-icon class="close-icon" name='close' size="12"></lit-icon><div class="no-close-icon" style="margin-right: 12px"></div>
3372                            </div>`;
3373                            }
3374
3375                        }
3376                    })
3377                    this.nav.innerHTML = navHtml;
3378                    this.initTabPos()
3379                    this.nav.querySelectorAll('.close-icon').forEach(a => {
3380                        a.onclick = (e) => {
3381                            e.stopPropagation();
3382                            const closeKey = e.target.parentElement.dataset.key;
3383                            console.log(closeKey);
3384                            console.log(e.target.parentElement.parentElement);
3385                            this.nav.removeChild(e.target.parentElement)
3386                            let elements = this.slots.assignedElements();
3387                            let closeElement = elements.filter(a => a.key === closeKey)[0];
3388                            closeElement.parentElement.removeChild(closeElement)
3389                            if (closeElement.style.display !== 'none') {
3390                                elements = this.slots.assignedElements();
3391                                let elArr = elements.filter(a => !a.hasAttribute('disabled'));
3392                                if (elArr.length > 0) {
3393                                    elArr[0].style.display = 'block';
3394                                    this.activekey = elArr[0].key
3395                                }
3396                            }
3397                        }
3398                    });
3399                })
3400                this.nav.onclick = (e) => {
3401                    if (e.target.closest('div').hasAttribute('data-disabled')) return;
3402                    let key = e.target.closest('div').dataset.key;
3403                    this.activeByKey(key)
3404                    let label = e.target.closest('div').querySelector('span').textContent;
3405                    this.dispatchEvent(new CustomEvent('onTabClick', { detail: { key: key, tab: label } }))
3406                };
3407            }
3408            set onTabClick(fn) {
3409                this.addEventListener('onTabClick', fn);
3410            }
3411            activeByKey(key) {
3412                if (key === null || key === undefined) return; //如果没有key 不做相应
3413                this.nav.querySelectorAll('.nav-item').forEach(a => {
3414                    if (a.getAttribute('data-key') === key) {
3415                        a.setAttribute('data-selected', 'true');
3416                    } else {
3417                        a.removeAttribute('data-selected');
3418                    }
3419                })
3420                let tbp = this.querySelector(`lit-tabpane[key='${key}']`);
3421                let panes = this.querySelectorAll('lit-tabpane');
3422                panes.forEach(a => {
3423                    if (a.key === key) {
3424                        a.style.display = 'block';
3425                        this.activekey = a.key;
3426                        this.initTabPos()
3427                    } else {
3428                        a.style.display = 'none';
3429                    }
3430                })
3431            }
3432            /*激活选中 key 对应的 pane  成功返回true,没有找到key对应的pane 返回false*/
3433            activePane(key) {
3434                if (key === null || key === undefined) return false;
3435                let tbp = this.querySelector(`lit-tabpane[key='${key}']`);
3436                if (tbp) {
3437                    this.activeByKey(key)
3438                    return true;
3439                } else {
3440                    return false;
3441                }
3442            }
3443            //当 custom element从文档DOM中删除时,被调用。
3444            disconnectedCallback() {
3445
3446            }
3447
3448            //当 custom element被移动到新的文档时,被调用。
3449            adoptedCallback() {
3450                console.log('Custom square element moved to new page.');
3451            }
3452
3453            //当 custom element增加、删除、修改自身属性时,被调用。
3454            attributeChangedCallback(name, oldValue, newValue) {
3455                if (name === 'activekey' && this.nav && oldValue !== newValue) {
3456                    this.activeByKey(newValue)
3457                }
3458            }
3459        }
3460        if (!customElements.get('lit-tabs')) {
3461            customElements.define('lit-tabs', LitTabs);
3462        }
3463
3464        class AppDiffFlame extends HTMLElement {
3465            draw;
3466            drawC;
3467            rowHeight = 17;
3468
3469            static get observedAttributes() {
3470                return []
3471            }
3472
3473            constructor() {
3474                super();
3475                const shadowRoot = this.attachShadow({ mode: 'open' });
3476                shadowRoot.innerHTML = `
3477        <style>
3478        :host{
3479            font-size:inherit;
3480            display:inline-flex;
3481            align-items: center;
3482            justify-content:center;
3483            height: 100%;
3484            width: 100%;
3485            margin-bottom: 40px;
3486        }
3487        canvas { border: 1px solid #e9e9e9; }
3488        #title{
3489            font-weight: bold;
3490            height: auto;
3491        }
3492        #title span{
3493            color: gray;
3494        }
3495        #funcNameSpan{
3496            display: inline-block;
3497            border: 1px solid #e9e9e9;
3498            box-sizing: border-box;
3499            border-radius: 2px;
3500            padding: 2px 8px;
3501            background: #fff;
3502             color: gray;
3503            flex: 3;
3504            margin-left: 10px;
3505        }
3506        #percentSpan{
3507            display: inline-block;
3508            border: 1px solid #e9e9e9;
3509            margin-left: 10px;
3510            box-sizing: border-box;
3511            border-radius: 2px;
3512            padding: 2px 8px;
3513            background: #fff;
3514            min-width: 160px;
3515            max-width: 160px;
3516            margin-left: 10px;
3517            width: 100px;
3518            height: 30px;
3519            color: gray;
3520        }
3521        #diffSpan{
3522            display: inline-block;
3523            border: 1px solid #e9e9e9;
3524            margin-left: 10px;
3525            box-sizing: border-box;
3526            border-radius: 2px;
3527            padding: 2px 8px;
3528            background: #fff;
3529            min-width: 160px;
3530            max-width: 160px;
3531            margin-left: 10px;
3532            width: 100px;
3533            height: 30px;
3534            color: gray;
3535        }
3536        #history{
3537            display: none;
3538            border: 1px solid #42b983;
3539            box-sizing: border-box;
3540            border-radius: 2px;
3541            padding: 2px 8px;
3542            background: #fff;
3543            width: 100px;
3544            margin-left: 10px;
3545            height: 30px;
3546            cursor: pointer;
3547            user-select: none;
3548        }
3549        #history:hover{
3550            background: #42b983;
3551            color: #fff;
3552        }
3553        #searchInput{
3554            height: 30px;
3555            margin-left: 10px;
3556            margin-right: 10px;
3557            flex: 1;
3558        }
3559        </style>
3560        <div style="position: relative;width: 100%">
3561            <div id="title">Process <span id="pid"></span> <span id="processName"></span> Thread <span id="tid"></span> <span id="threadName"></span><span id="sample"></span></div>
3562            <canvas id="panel" title=""></canvas>
3563            <div id="controller" style="position: absolute;top: 32px;display: flex;width: 100%">
3564                <span id="history">Zoom Out</span>
3565                <span id="funcNameSpan"></span>
3566                <span id="diffSpan"></span>
3567                <span id="percentSpan"></span>
3568                <lit-input id="searchInput" placeholder="search"  allow-clear>Search</lit-input>
3569            </div>
3570        </div>
3571        <slot></slot>
3572        `
3573            }
3574
3575            connectedCallback() {
3576                this.history = [];
3577                this.historyRefer = [];
3578                this.panel = this.shadowRoot.getElementById('panel');
3579                this.controller = this.shadowRoot.getElementById('controller');
3580                this.funcNameSpan = this.shadowRoot.getElementById('funcNameSpan');
3581                this.percentSpan = this.shadowRoot.getElementById('percentSpan');
3582                this.diffSpan = this.shadowRoot.getElementById('diffSpan');
3583                this.historySpan = this.shadowRoot.getElementById('history');
3584                this.searchInput = this.shadowRoot.getElementById('searchInput');
3585                this.pid = this.shadowRoot.getElementById('pid');
3586                this.tid = this.shadowRoot.getElementById('tid');
3587                this.processName = this.shadowRoot.getElementById('processName');
3588                this.threadName = this.shadowRoot.getElementById('threadName');
3589                this.sample = this.shadowRoot.getElementById('sample');
3590                this.titleDiv = this.shadowRoot.getElementById('title');
3591                this.context = this.panel.getContext('2d');
3592                this.panel.width = this.shadowRoot.host.clientWidth;
3593                this.panel.height = this.shadowRoot.host.clientHeight;
3594                this.historySpan.onclick = e => {
3595                    if (this.history.length > 2) {
3596                        this.history.pop();
3597                        this.historyRefer.pop();
3598                        this.zoomOutRefer(this.historyRefer[this.historyRefer.length - 1])
3599                        this.zoomOut(this.history[this.history.length - 1])
3600                    } else if (this.history.length == 2) {
3601                        this.history.pop();
3602                        this.historyRefer.pop();
3603                        this.zoomOutRefer(this.historyRefer[this.historyRefer.length - 1])
3604                        this.zoomOut(this.history[this.history.length - 1])
3605                        this.historySpan.style.display = 'none';
3606                    } else {
3607                        this.historySpan.style.display = 'none';
3608                    }
3609                }
3610                this.searchInput.addEventListener("onPressEnter", (e) => {
3611                    this.keyword = e.currentTarget.value;
3612                    requestAnimationFrame(this.draw);
3613                })
3614                this.searchInput.addEventListener('onClear', e => {
3615                    this.keyword = null;
3616                    requestAnimationFrame(this.draw);
3617                })
3618                this.panel.onmouseover = (e) => {
3619                    this.mouseState = 'mouseOver';
3620                }
3621                this.panel.onmouseleave = e => {
3622                    this.mouseState = 'mouseLeave';
3623                    this.mouseX = 0;
3624                    this.mouseY = 0;
3625                    requestAnimationFrame(this.draw)
3626                }
3627                this.panel.onmousemove = e => {
3628                    const pos = e.currentTarget.getBoundingClientRect();
3629                    this.mouseX = e.clientX - pos.left;
3630                    this.mouseY = e.clientY - pos.top;
3631                    this.mouseState = 'mouseMove';
3632                    requestAnimationFrame(this.draw)
3633                }
3634                this.panel.onmousedown = e => {
3635                    this.mouseState = 'mouseDown';
3636                    // const pos = e.currentTarget.getBoundingClientRect();
3637                    // this.mouseX = e.clientX - pos.left;
3638                    // this.mouseY = e.clientY - pos.top;
3639                    // requestAnimationFrame(this.draw)
3640                }
3641                this.panel.onmouseup = e => {
3642                    this.mouseState = 'mouseUp';
3643                    const pos = e.currentTarget.getBoundingClientRect();
3644                    this.mouseX = e.clientX - pos.left;
3645                    this.mouseY = e.clientY - pos.top;
3646                    requestAnimationFrame(this.draw)
3647                }
3648                // this.panel.onclick = (e)=>{
3649                //     this.mouseState = 'mouseClick';
3650                // }
3651            }
3652
3653            set data(value) {
3654                this._data = value;
3655                this._dataRefer = value.jsonRefer;
3656                /**
3657                 * type 值默认为1
3658                 * 1 Show percentage of event count relative to the current thread
3659                 * 2 Show percentage of event count relative to the current process
3660                 * 3 Show percentage of event count relative to all process
3661                 * 4 show event count
3662                 * 5 show event count in milliseconds
3663                 * 其他值
3664                 * pid, processName, tid, threadName, eventCount, sampleCount, CallOrder(g节点)
3665                 */
3666                this.type = value.type;
3667                // reverse 表示火焰图需要是否需要倒序绘制,value中没有传reverse,默认表示false 既正序绘制
3668                this.reverse = value.reverse || false;
3669                // CallOrder.symbol 如果为-1 表示为根节点
3670                if (value.CallOrder.symbol === -1) {
3671                    this._c = value.CallOrder.callStack;
3672                    this._cRef = this.threadRefer.CallOrder.callStack;
3673                } else {
3674                    this._c = [value.CallOrder];
3675                    this._cRef = [this.threadRefer.CallOrder];
3676                }
3677                // console.log(this.data.json, this.dataRefer)
3678                // console.log(this.c, this.cRef)
3679                this.history.push(this._c)
3680                this.historyRefer.push(this.cRef)
3681                // 获取选项的 eventCount值 例如:hw-cpu-cycles 和 processes 同层级节点  表示所有进程的eventCount值求和
3682                this.eventCountAllProcess = this._data.json.recordSampleInfo[window.eventIndex].eventCount
3683                console.log("diff eventCountAllProcess = " + this.eventCountAllProcess)
3684                if (value.pid) {
3685                    // 当前进程的 eventCount值,没有直接送json中去取是因为,第一次传的json 和 windows.data 一样,后续点击了 自节点传入的是子集,可能取的不准确
3686                    this.eventCountCurrentProcess = this._data.json.recordSampleInfo[window.eventIndex].processes.filter(it => it.pid === value.pid)[0].eventCount
3687                    console.log("diff eventCountCurrentProcess = " + this.eventCountCurrentProcess)
3688                    // 将进程号显示在界面上
3689                    this.pid.textContent = value.pid;
3690                } else {
3691                    // this.titleDiv.style.display = 'none';
3692                }
3693                if (value.tid) {
3694                    // 取当前线程的 eventCount 值
3695                    this.eventCountCurrentThread = this._data.json.recordSampleInfo[window.eventIndex].processes.filter(it => it.pid === value.pid)[0].threads.filter(it => it.tid === value.tid)[0].eventCount;
3696                    this.eventCountCurrentThreadRefer = this._data.jsonRefer.recordSampleInfo[window.eventIndex].processes.filter(it => it.pid === value.pid)[0].threads.filter(it => it.tid === value.tid)[0].eventCount;
3697                    console.log("diff eventCountCurrentThread = " + this.eventCountCurrentThread)
3698                    // 显示线程号
3699                    this.tid.textContent = value.tid;
3700                }
3701                if (value.sampleCount) {
3702                    //显示sampleCount值 ,这个值在 thread节点里,只有线程有
3703                    this.sample.textContent = ' (Samples: ' + value.sampleCount + ")";
3704                }
3705                if (value.processName) {
3706                    //显示进程名 null显示空字符串
3707                    this.processName.textContent = value.processName ? `(${value.processName})` : '';
3708                }
3709                if (value.threadName) {
3710                    //显示线程名 null显示空字符串
3711                    this.threadName.textContent = value.threadName ? `(${value.threadName})` : '';
3712                }
3713                if (value.funcName) {
3714                    //如果是单独绘制Funtion Tab界面下的火焰图,只有 函数名称, 上面的 的进程线程信息不显示
3715                    this.titleDiv.innerHTML = `${value.funcName}`
3716                    this.controller.style.top = `${this.titleDiv.clientHeight + 10}px`
3717                }
3718                //获取最大层级,用于做火焰图高度计算,点击子节点后 maxDepth 也对应变化
3719                this.maxDepth = this.getMaxDepth(this.data.CallOrder.callStack) + 5; //设置最大层级
3720                this.maxDepthRefer = this.getMaxDepth(this.threadRefer.CallOrder.callStack) + 5; //设置最大层级
3721                console.log("diff maxDepth = " + this.maxDepth + " maxDepthRefer = " + this.maxDepthRefer)
3722                this.sumCount = this.data.CallOrder.subEvents; //设置根节点的 sumCount
3723                this.sumCountRefer = this.threadRefer.CallOrder.subEvents; //设置根节点的 sumCountRefer 用来做对比
3724                console.log("diff sumCount = " + this.sumCount + " sumCountRefer = " + this.sumCountRefer)
3725                // console.log(this.threadRefer)//对比的线程数据
3726                //计算canvas的宽度和高度
3727                this.panel.height = this.maxDepth * this.rowHeight
3728                this.panel.width = this.shadowRoot.host.clientWidth
3729                //设置canvas 的大小
3730                this.makeHighRes(this.panel);
3731                //开始调用draw方法绘制一帧
3732                requestAnimationFrame(this.draw);
3733            }
3734
3735            get data() {
3736                return this._data;
3737            }
3738
3739            //需要对比的原始数据
3740            get dataRefer() {
3741                return this._dataRefer;
3742            }
3743
3744            set dataRefer(val) {
3745                this._dataRefer = val;
3746            }
3747
3748            /*
3749              获取要对比的 thread数据
3750             */
3751            get threadRefer() {
3752                return this._threadRefer;
3753            }
3754
3755            /**
3756             * 设置要对比的数据 这里传入的是 thread 节点;包含 {tid,eventCount,sampleCount,libs:[],CallOrder:[],CalledOrder:[]}
3757             * @param val
3758             */
3759            set threadRefer(val) {
3760                this._threadRefer = val;
3761            }
3762
3763            /**
3764             * 点击图形中子节点时 传入 当前节点当作根节点绘制
3765             * @param value
3766             */
3767            set c(value) {
3768                //显示回退button
3769                this.historySpan.style.display = 'block';
3770                //设置相对根节点
3771                this._c = value;
3772                //添加历史记录,用于回退到上一图形状态
3773                this.history.push(this._c)
3774                // 下面代码实现 canvas 随内容高度变化
3775                // this.maxDepth = this._getMaxDepth(value) + 5; //设置最大层级
3776                // this.panel.height = this.maxDepth * this.rowHeight
3777                // this.panel.width = this.shadowRoot.host.clientWidth;
3778                // this.makeHighRes(this.panel);
3779                //开始绘制
3780                requestAnimationFrame(this.draw);
3781            }
3782
3783            get c() {
3784                return this._c;
3785            }
3786
3787            get cRef() {
3788                return this._cRef
3789            }
3790
3791            set cRef(val) {
3792                this._cRef = val;
3793                this.historyRefer.push(this._cRef)
3794            }
3795
3796            zoomOutRefer(value) {
3797                this._cRef = value;
3798            }
3799            zoomOut(value) {
3800                this._c = value;
3801                // 下面代码实现 canvas 随内容高度变化
3802                // this.maxDepth = this._getMaxDepth(value) + 5; //设置最大层级
3803                // // this.sumCount = value.reduce((acc, cur) => acc + cur.s, 0); //设置根节点的 sumCount
3804                // this.panel.height = this.maxDepth * this.rowHeight
3805                // this.panel.width = this.shadowRoot.host.clientWidth;
3806                // this.makeHighRes(this.panel);
3807                requestAnimationFrame(this.draw);
3808            }
3809
3810            makeHighRes(canvas) {
3811                let ctx = canvas.getContext('2d');
3812                let dpr = window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1;
3813                let oldWidth = canvas.width;
3814                let oldHeight = canvas.height;
3815                canvas.width = Math.round(oldWidth * dpr);
3816                canvas.height = Math.round(oldHeight * dpr);
3817                canvas.style.width = oldWidth + 'px';
3818                canvas.style.height = oldHeight + 'px';
3819                ctx.scale(dpr, dpr);
3820                this.context = ctx;
3821                return ctx;
3822            }
3823
3824            //绘制火焰图
3825            draw = () => {
3826                // requestAnimationFrame(this.draw);
3827                let ctx = this.context;
3828                // ctx.clearRect(0, 0, this.panel.width, this.panel.height);
3829                //绘制背景渐变色
3830                let grad = ctx.createLinearGradient(0, 0, 0, this.panel.height / 2); //创建一个渐变色线性对象
3831                grad.addColorStop(0, "#eeeeee");                  //定义渐变色颜色
3832                grad.addColorStop(1, "#efefb1");
3833                ctx.fillStyle = grad;                         //设置fillStyle为当前的渐变对象
3834                ctx.fillRect(0, 0, this.panel.width, this.panel.height);
3835                // console.log(this.c,this.cRef);
3836                if (this.data) {
3837                    //如果需要倒序绘制调用drawCReverse方法绘制
3838                    if (this.reverse) {
3839                        this.drawCReverse(
3840                            this.c,
3841                            1,
3842                            {
3843                                x: 0,
3844                                y: this.rowHeight * 4,
3845                                w: this.panel.clientWidth,
3846                                h: this.rowHeight
3847                            });
3848                    } else {
3849                        //绘制正序图,需要3个参数  节点数据,当前绘制的层级,当前这行的Rect数据
3850                        this.drawC(
3851                            this.c,//根节点
3852                            this.cRef,
3853                            1,
3854                            {
3855                                x: 0,
3856                                y: this.panel.clientHeight - this.rowHeight,
3857                                w: this.panel.clientWidth,
3858                                h: this.rowHeight
3859                            });
3860                    }
3861                }
3862            }
3863
3864            //根据type类型计算 统计数据
3865            getStatistics(c) {
3866                let statistics;//鼠标hover展示的百分比
3867                switch (this.type) {
3868                    case 1: //current thread
3869                        statistics = c.subEvents * 100 / this.eventCountCurrentThread;
3870                        statistics = statistics.toFixed(2)
3871                        statistics = `${statistics}%`
3872                        break;
3873                    case 2: // current process
3874                        statistics = c.subEvents * 100 / this.eventCountCurrentProcess;
3875                        statistics = statistics.toFixed(2)
3876                        statistics = `${statistics}%`
3877                        break;
3878                    case 3: // all process
3879                        statistics = c.subEvents * 100 / this.eventCountAllProcess;
3880                        statistics = statistics.toFixed(2)
3881                        statistics = `${statistics}%`
3882                        break;
3883                    case 4: //event count
3884                        statistics = c.subEvents;
3885                        statistics = `${statistics}`
3886                        break;
3887                    case 5: //event count in milliseconds
3888                        statistics = c.subEvents / 1000000;
3889                        statistics = statistics.toFixed(3)
3890                        statistics = `${statistics} ms`
3891                        break;
3892                    default: //current thread
3893                        statistics = c.subEvents * 100 / this.eventCountCurrentThread;
3894                        statistics = statistics.toFixed(2)
3895                        statistics = `${statistics}%`
3896                        break;
3897                }
3898                return statistics;
3899            }
3900
3901            //HTML反转义
3902            htmlDecode(text) {
3903                let temp = document.createElement("div");
3904                temp.innerHTML = text;
3905                let output = temp.innerText || temp.textContent;
3906                temp = null;
3907                return output;
3908            }
3909
3910            //获取方法名
3911            getFunctionName(f) {
3912                let funName = "";
3913                if (this._data.json.SymbolMap[f]) {
3914                    funName = this._data.json.SymbolMap[f].symbol;
3915                } else {
3916                    let f = c[i].symbol;
3917                    console.log(`processId:${this.pid.textContent} processName:${this.processName.textContent} threadId:${this.tid.textContent} threadName:${this.threadName.textContent}`, c[i], "SymbolMap中没有对应的值")
3918                }
3919                return this.htmlDecode(funName);
3920            }
3921
3922            //根据比对对象的funId 在比对数组中找到对应的函数名
3923            getFunctionNameRefer(f) {
3924                let funName = "";
3925                if (this.dataRefer.SymbolMap[f]) {
3926                    funName = this.dataRefer.SymbolMap[f].symbol;
3927                } else {
3928                    // let f = c[i].symbol;
3929                    // console.log(`processId:${this.pid.textContent} processName:${this.processName.textContent} threadId:${this.tid.textContent} threadName:${this.threadName.textContent}`, c[i], "SymbolMap中没有对应的值")
3930                }
3931                return this.htmlDecode(funName);
3932            }
3933
3934            //根据百分比获取显示的背景色,如果 搜索框中的关键字与当前funcName 匹配则单独显示色值
3935            getColor(percent2, funName) {
3936                let heatColor;
3937                if (this.keyword && this.keyword.length > 0 && funName.indexOf(this.keyword) != -1) {
3938                    heatColor = { r: 0x66, g: 0xad, b: 0xff };
3939                } else {
3940                    heatColor = this.getHeatColor(percent2);
3941                }
3942                return heatColor;
3943            }
3944
3945            //倒序绘制
3946            drawCReverse = (c, dept, rect) => {
3947                let ctx = this.context;
3948                let offset = 0
3949                for (let i = 0; i < c.length; i++) {
3950                    let funName = this.getFunctionName(c[i].symbol);
3951                    let funcId = c[i].symbol;
3952                    let percent = c[i].subEvents * 100 / (c.reduce((acc, cur) => acc + cur.subEvents, 0));
3953                    let percent2 = c[i].subEvents * 100 / this.sumCount;
3954                    if (percent2 < 0.1) continue //过滤掉 百分比为0.1一下的节点
3955                    let heatColor = this.getColor(percent2, funName);
3956                    let w = rect.w * (percent / 100.0);
3957                    if (w < 1) {
3958                        w = 1
3959                    }
3960                    let _x = rect.x + offset;
3961                    //绘制填充矩形
3962                    ctx.fillStyle = `rgba(${heatColor.r}, ${heatColor.g}, ${heatColor.b}, 1)`;
3963                    ctx.fillRect(_x, rect.y + 2, w, rect.h - 2);
3964                    // 绘制文本
3965                    ctx.fillStyle = "rgba(0,0,0,1)";
3966                    let txtWidth = ctx.measureText(funName).width;//文本长度
3967                    let chartWidth = txtWidth / funName.length;//每个字符长度
3968                    let number = (w - 6) / chartWidth;//可以显示多少字符
3969                    if (number >= 4 && number < funName.length - 3) {
3970                        ctx.fillText(funName.slice(0, number - 3) + '...', _x + 3, rect.y + 13, w - 6)
3971                    } else if (number >= 4) {
3972                        ctx.fillText(funName, _x + 3, rect.y + 13, w - 6)
3973                    }
3974                    let _rect = {
3975                        x: _x, y: rect.y, w: w, h: rect.h
3976                    }
3977                    c[i].rect = _rect;
3978
3979                    if (this.mouseX > _x && this.mouseX < _x + w && this.mouseY > rect.h * 3 + (dept) * rect.h && this.mouseY < rect.h * 3 + (dept + 1) * rect.h) {
3980                        if (this.mouseState === 'mouseMove') {
3981                            //绘制边框矩形
3982                            // ctx.font = '12px serif';
3983                            ctx.lineWidth = 2;
3984                            ctx.strokeStyle = `#000000`;
3985                            ctx.strokeRect(_x, rect.y + 1, w - 1, rect.h);
3986                            this.funcNameSpan.textContent = funName
3987                            this.panel.title = funName
3988                            this.percentSpan.textContent = this.getStatistics(c[i]);
3989                        } else {
3990                            if (this.mouseState === 'mouseUp') {
3991                                this.mouseState = null;
3992                                if (!this.compareNodes(this.c, [c[i]])) {
3993                                    this.c = [c[i]];
3994                                }
3995                            }
3996                        }
3997                    } else {
3998                        ctx.lineWidth = 1;
3999                        // ctx.font = '11px serif';
4000                    }
4001                    offset += w;
4002                    // console.log(_rect,dept,percent,funName);
4003                    //递归绘制子节点
4004                    if (c[i].callStack && c[i].callStack.length > 0) {
4005                        _rect.y = _rect.y + _rect.h
4006                        this.drawCReverse(c[i].callStack, dept + 1, _rect);
4007                    }
4008                }
4009            }
4010
4011            //正序绘制
4012            drawC = (c, cRef, dept, rect) => {
4013                let ctx = this.context;
4014                let offset = 0//用来保存x坐标的偏移量,保证在rect的矩形范围内从左往右绘制 c集合中的节点
4015                for (let i = 0; i < c.length; i++) {
4016                    let funName = this.getFunctionName(c[i].symbol);
4017                    // console.log(dept,this.data.SymbolMap[c[i].symbol].symbol);
4018                    let funcId = c[i].symbol;//获取funcId
4019                    //计算这个节点的subEvents值 在当前传入集合的subEvents总和中占的百分比,因为rect默认是最大宽宽的,递归后最越来越小,根据这个百分比和 rect矩形的宽度计算节点绘制的宽度
4020                    let percent = c[i].subEvents * 100 / (c.reduce((acc, cur) => acc + cur.subEvents, 0));//sumCount;
4021                    //计算这个节点的subEvents 在总的sumCount(对应的是最大宽度)中占比,这个百分比用来计算颜色,占比越少颜色越浅
4022                    let percent2 = c[i].subEvents * 100 / this.sumCount;
4023                    if (percent2 < 0.1) continue //过滤掉 百分比为0.1一下的节点
4024                    let heatColor = this.getColor(percent2, funName);
4025                    let w = rect.w * (percent / 100.0);
4026                    if (w < 1) {
4027                        w = 1
4028                    }
4029                    let _x = rect.x + offset;
4030                    //绘制填充矩形
4031                    ctx.fillStyle = `rgba(${heatColor.r}, ${heatColor.g}, ${heatColor.b}, 1)`;
4032                    let __rect = { x: _x, y: rect.y + 2, w: w, h: rect.h - 2 }
4033                    ctx.fillRect(__rect.x, __rect.y, __rect.w, __rect.h);
4034
4035                    //覆盖要比较的数据 (显示的方法名,显示的矩形对象,宽度的百分比,需要比对节点集合[需要根据funName从中取得要比较的节点])
4036                    let nodeRefer = this.drawReferData(ctx, funName, __rect, percent, cRef);
4037
4038                    // 绘制文本
4039                    ctx.fillStyle = "rgba(0,0,0,1)";
4040                    let txtWidth = ctx.measureText(funName).width;//文本长度
4041                    let chartWidth = txtWidth / funName.length;//每个字符长度
4042                    let number = (w - 6) / chartWidth;//可以显示多少字符
4043                    if (number >= 4 && number < funName.length - 3) {
4044                        ctx.fillText(funName.slice(0, number - 3) + '...', _x + 3, rect.y + 13, w - 6)
4045                    } else if (number >= 4) {
4046                        ctx.fillText(funName, _x + 3, rect.y + 13, w - 6)
4047                    }
4048                    let _rect = {
4049                        x: _x, y: rect.y, w: w, h: rect.h
4050                    }
4051                    c[i].rect = _rect;
4052
4053                    if (this.mouseX > _x && this.mouseX < _x + w && this.mouseY > (this.maxDepth - dept) * rect.h && this.mouseY < (this.maxDepth - dept + 1) * rect.h) {
4054                        if (this.mouseState === 'mouseMove') {
4055                            //绘制边框矩形
4056                            // ctx.font = '12px serif';
4057                            ctx.lineWidth = 2;
4058                            ctx.strokeStyle = `#000000`;
4059                            ctx.strokeRect(_x, rect.y + 1, w - 1, rect.h);
4060                            this.funcNameSpan.textContent = funName
4061                            this.panel.title = funName
4062                            this.percentSpan.textContent = this.getStatistics(c[i]);
4063                            let node = this.fetchSameFuncNameData(funName, cRef);
4064                            var diff = 0.0;
4065                            if (node) {
4066                                let percentRefer = node.subEvents * 100 / (cRef.reduce((acc, cur) => acc + cur.subEvents, 0))
4067                                diff = (percent - percentRefer).toFixed(2)
4068                            }
4069                            this.diffSpan.textContent = `diff: ${diff}%`;
4070                        } else {
4071                            if (this.mouseState === 'mouseUp') {
4072                                this.mouseState = null;
4073                                if (!this.compareNodes(this.c, [c[i]])) {
4074                                    this.c = [c[i]];
4075                                    this.cRef = [nodeRefer];
4076                                }
4077                            }
4078                        }
4079                    } else {
4080                        ctx.lineWidth = 1;
4081                        // ctx.font = '11px serif';
4082                    }
4083                    offset += w;
4084                    // console.log(_rect,dept,percent,funName);
4085                    //递归绘制子节点
4086                    if (c[i].callStack && c[i].callStack.length > 0) {
4087                        _rect.y = _rect.y - _rect.h
4088                        this.drawC(c[i].callStack, nodeRefer ? nodeRefer.callStack : [], dept + 1, _rect);
4089                    }
4090                }
4091            }
4092
4093            //在对比数组中找到 方法名与 funName相同的节点
4094            fetchSameFuncNameData(funName, cRef) {
4095                let node = cRef.filter(it => {
4096                    let funNameRefer = this.getFunctionNameRefer(it.symbol);
4097                    return funName === funNameRefer
4098                })
4099                // console.log("cRef:",funName,cRef,node)
4100                if (node && node.length > 0) {
4101                    return node[0];
4102                } else {
4103                    return null;
4104                }
4105            }
4106
4107            //对比数据,如果要比对的数据宽度大于绘制节点宽度 显示红色,否则显示蓝色,颜色深度取决于大多少或者小多少
4108            drawReferData(ctx, funName, rect, percent, cRef) {
4109                //根据funName 在cRef集合中找到同函数名的节点
4110                let node = this.fetchSameFuncNameData(funName, cRef);
4111                if (node) {
4112                    let percentRefer = node.subEvents * 100 / (cRef.reduce((acc, cur) => acc + cur.subEvents, 0));//sumCount;
4113                    // console.log("找到相同节点", funName, rect.w, percent, percentRefer, node)
4114                    //如果绘制的百分比小于要比较的百分比
4115                    if (percent < percentRefer) {
4116                        let offset = Math.abs(percent - percentRefer);
4117                        if (offset > 0 && offset <= 10) {
4118                            ctx.fillStyle = `#66B3FF`;
4119                        } else if (offset > 10 && offset <= 20) {
4120                            ctx.fillStyle = `#2894FF`;
4121                        } else if (offset > 20 && offset <= 30) {
4122                            ctx.fillStyle = `#0072E3`;
4123                        } else if (offset > 30 && offset <= 50) {
4124                            ctx.fillStyle = `#0066CC`;
4125                        } else {
4126                            ctx.fillStyle = `#005AB5`;
4127                        }
4128                        ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
4129                    } else if (percent > percentRefer) {
4130                        let offset = Math.abs(percent - percentRefer);
4131                        if (offset > 0 && offset <= 10) {
4132                            ctx.fillStyle = `#FF7575`;
4133                        } else if (offset > 10 && offset <= 20) {
4134                            ctx.fillStyle = `#FF2D2D`;
4135                        } else if (offset > 20 && offset <= 30) {
4136                            ctx.fillStyle = `#EA0000`;
4137                        } else if (offset > 30 && offset <= 50) {
4138                            ctx.fillStyle = `#CE0000`;
4139                        } else {
4140                            ctx.fillStyle = `#AE0000`;
4141                        }
4142                        ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
4143                    }
4144                }
4145                return node;
4146            }
4147
4148
4149            compareNode(na, nb) {
4150                let res = false;
4151                if (na.selfEvents === nb.selfEvents && na.subEvents === nb.subEvents && na.symbol === nb.symbol) {
4152                    res = this.compareNodes(na.callStack || [], nb.callStack || []);
4153                }
4154                return res;
4155            }
4156
4157            compareNodes(a, b) {
4158                let res = false;
4159                if (a.length === b.length) {
4160                    if (a.length === 0) {
4161                        return true;
4162                    }
4163                    for (let i = 0; i < a.length; i++) {
4164                        res = this.compareNode(a[i], b[i])
4165                    }
4166                }
4167                return res;
4168            }
4169
4170            getMaxDepth(nodes) {
4171                let isArray = Array.isArray(nodes);
4172                let sumCount;
4173                if (isArray) {
4174                    sumCount = nodes.reduce((acc, cur) => acc + cur.subEvents, 0);
4175                } else {
4176                    sumCount = nodes.subEvents;
4177                }
4178                let width = sumCount * 100.0 / this.sumCount;
4179                if (width < 0.1) {
4180                    return 0;
4181                }
4182                let children = isArray ? this.splitChildrenForNodes(nodes) : nodes.callStack;
4183                let childDepth = 0;
4184                if (children) {
4185                    for (let child of children) {
4186                        childDepth = Math.max(childDepth, this.getMaxDepth(child));
4187                    }
4188                }
4189                return childDepth + 1;
4190            }
4191
4192            splitChildrenForNodes(nodes) {
4193                let map = new Map();
4194                for (let node of nodes) {
4195                    for (let child of node.callStack) {
4196                        let subNodes = map.get(child.symbol);
4197                        if (subNodes) {
4198                            subNodes.push(child);
4199                        } else {
4200                            map.set(child.symbol, [child]);
4201                        }
4202                    }
4203                }
4204                let res = [];
4205                for (let subNodes of map.values()) {
4206                    res.push(subNodes.length == 1 ? subNodes[0] : subNodes);
4207                }
4208                return res;
4209            }
4210
4211            //根据百分比获取节点的背景色
4212            getHeatColor(widthPercentage) {
4213                return {
4214                    r: Math.floor(245 + 10 * (1 - widthPercentage * 0.01)),
4215                    g: Math.floor(110 + 105 * (1 - widthPercentage * 0.01)),
4216                    b: 100,
4217                };
4218            }
4219
4220            attributeChangedCallback(name, oldValue, newValue) {
4221            }
4222        }
4223        if (!customElements.get('app-diff-flame')) {
4224            customElements.define('app-diff-flame', AppDiffFlame);
4225        }
4226
4227        class AppNormalFlame extends HTMLElement {
4228            draw;
4229            drawC;
4230            rowHeight = 17;
4231
4232            static get observedAttributes() {
4233                return []
4234            }
4235
4236            constructor() {
4237                super();
4238                const shadowRoot = this.attachShadow({ mode: 'open' });
4239                shadowRoot.innerHTML = `
4240        <style>
4241        :host{
4242            font-size:inherit;
4243            display:inline-flex;
4244            align-items: center;
4245            justify-content:center;
4246            height: 100%;
4247            width: 100%;
4248            margin-bottom: 40px;
4249        }
4250        canvas { border: 1px solid #e9e9e9; }
4251        #title{
4252            font-weight: bold;
4253            height: auto;
4254        }
4255        #title span{
4256            color: gray;
4257        }
4258        #funcNameSpan{
4259            display: inline-block;
4260            border: 1px solid #e9e9e9;
4261            box-sizing: border-box;
4262            border-radius: 2px;
4263            padding: 2px 8px;
4264            background: #fff;
4265             color: gray;
4266            flex: 3;
4267            margin-left: 10px;
4268        }
4269        #percentSpan{
4270            display: inline-block;
4271            border: 1px solid #e9e9e9;
4272            margin-left: 10px;
4273            box-sizing: border-box;
4274            border-radius: 2px;
4275            padding: 2px 8px;
4276            background: #fff;
4277            min-width: 160px;
4278            max-width: 160px;
4279            margin-left: 10px;
4280            width: 100px;
4281            height: 30px;
4282            color: gray;
4283        }
4284        #history{
4285            display: none;
4286            border: 1px solid #42b983;
4287            box-sizing: border-box;
4288            border-radius: 2px;
4289            padding: 2px 8px;
4290            background: #fff;
4291            width: 100px;
4292            margin-left: 10px;
4293            height: 30px;
4294            cursor: pointer;
4295            user-select: none;
4296        }
4297        #history:hover{
4298            background: #42b983;
4299            color: #fff;
4300        }
4301        #searchInput{
4302            height: 30px;
4303            margin-left: 10px;
4304            margin-right: 10px;
4305            flex: 1;
4306        }
4307        </style>
4308        <div style="position: relative;width: 100%">
4309            <div id="title">Process <span id="pid"></span> <span id="processName"></span> Thread <span id="tid"></span> <span id="threadName"></span><span id="sample"></span></div>
4310            <canvas id="panel" title=""></canvas>
4311            <div id="controller" style="position: absolute;top: 32px;display: flex;width: 100%">
4312                <span id="history">Zoom Out</span>
4313                <span id="funcNameSpan"></span>
4314                <span id="percentSpan"></span>
4315                <lit-input id="searchInput" placeholder="search"  allow-clear>Search</lit-input>
4316            </div>
4317        </div>
4318        <slot></slot>
4319        `
4320            }
4321
4322            connectedCallback() {
4323                this.history = [];
4324                this.panel = this.shadowRoot.getElementById('panel');
4325                this.controller = this.shadowRoot.getElementById('controller');
4326                this.funcNameSpan = this.shadowRoot.getElementById('funcNameSpan');
4327                this.percentSpan = this.shadowRoot.getElementById('percentSpan');
4328                this.historySpan = this.shadowRoot.getElementById('history');
4329                this.searchInput = this.shadowRoot.getElementById('searchInput');
4330                this.pid = this.shadowRoot.getElementById('pid');
4331                this.tid = this.shadowRoot.getElementById('tid');
4332                this.processName = this.shadowRoot.getElementById('processName');
4333                this.threadName = this.shadowRoot.getElementById('threadName');
4334                this.sample = this.shadowRoot.getElementById('sample');
4335                this.titleDiv = this.shadowRoot.getElementById('title');
4336                this.context = this.panel.getContext('2d');
4337                this.panel.width = this.shadowRoot.host.clientWidth;
4338                this.panel.height = this.shadowRoot.host.clientHeight;
4339                this.historySpan.onclick = e => {
4340                    if (this.history.length > 2) {
4341                        this.history.pop();
4342                        this.zoomOut(this.history[this.history.length - 1])
4343                    } else if (this.history.length == 2) {
4344                        this.history.pop();
4345                        this.zoomOut(this.history[this.history.length - 1])
4346                        this.historySpan.style.display = 'none';
4347                    } else {
4348                        this.historySpan.style.display = 'none';
4349                    }
4350                }
4351                this.searchInput.addEventListener("onPressEnter", (e) => {
4352                    this.keyword = e.currentTarget.value;
4353                    requestAnimationFrame(this.draw);
4354                })
4355                this.searchInput.addEventListener('onClear', e => {
4356                    this.keyword = null;
4357                    requestAnimationFrame(this.draw);
4358                })
4359                this.panel.onmouseover = (e) => {
4360                    this.mouseState = 'mouseOver';
4361                }
4362                this.panel.onmouseleave = e => {
4363                    this.mouseState = 'mouseLeave';
4364                    this.mouseX = 0;
4365                    this.mouseY = 0;
4366                    requestAnimationFrame(this.draw)
4367                }
4368                this.panel.onmousemove = e => {
4369                    const pos = e.currentTarget.getBoundingClientRect();
4370                    this.mouseX = e.clientX - pos.left;
4371                    this.mouseY = e.clientY - pos.top;
4372                    this.mouseState = 'mouseMove';
4373                    requestAnimationFrame(this.draw)
4374                }
4375                this.panel.onmousedown = e => {
4376                    this.mouseState = 'mouseDown';
4377                    // const pos = e.currentTarget.getBoundingClientRect();
4378                    // this.mouseX = e.clientX - pos.left;
4379                    // this.mouseY = e.clientY - pos.top;
4380                    // requestAnimationFrame(this.draw)
4381                }
4382                this.panel.onmouseup = e => {
4383                    this.mouseState = 'mouseUp';
4384                    const pos = e.currentTarget.getBoundingClientRect();
4385                    this.mouseX = e.clientX - pos.left;
4386                    this.mouseY = e.clientY - pos.top;
4387                    requestAnimationFrame(this.draw)
4388                }
4389                // this.panel.onclick = (e)=>{
4390                //     this.mouseState = 'mouseClick';
4391                // }
4392            }
4393
4394            set data(value) {
4395                this._data = value;
4396                this.type = value.type;
4397                this.reverse = value.reverse || false;
4398                if (value.CallOrder.symbol === -1) {
4399                    this._c = value.CallOrder.callStack;
4400                } else {
4401                    this._c = [value.CallOrder];
4402                }
4403                this.history.push(this._c)
4404                this.eventCountAllProcess = value.json.recordSampleInfo[window.eventIndex].eventCount
4405                if (value.pid) {
4406                    this.eventCountCurrentProcess = value.json.recordSampleInfo[window.eventIndex].processes.filter(it => it.pid === value.pid)[0].eventCount
4407                    this.pid.textContent = value.pid;
4408                } else {
4409                    // this.titleDiv.style.display = 'none';
4410                }
4411                if (value.tid) {
4412                    this.eventCountCurrentThread = value.json.recordSampleInfo[window.eventIndex].processes.filter(it => it.pid === value.pid)[0].threads.filter(it => it.tid === value.tid)[0].eventCount;
4413                    this.tid.textContent = value.tid;
4414                }
4415                if (value.sampleCount) {
4416                    this.sample.textContent = ' (Samples: ' + value.sampleCount + ")";
4417                }
4418                if (value.processName) {
4419                    this.processName.textContent = value.processName ? `(${value.processName})` : '';
4420                }
4421                if (value.threadName) {
4422                    this.threadName.textContent = value.threadName ? `(${value.threadName})` : '';
4423                }
4424                if (value.funcName) {
4425                    this.titleDiv.innerHTML = `${value.funcName}`
4426                    this.controller.style.top = `${this.titleDiv.clientHeight + 10}px`
4427                }
4428                this.maxDepth = this.getMaxDepth(this.data.CallOrder.callStack) + 5; //设置最大层级
4429                this.sumCount = this.data.CallOrder.subEvents; //设置根节点的 sumCount
4430                this.panel.height = this.maxDepth * this.rowHeight
4431                this.panel.width = this.shadowRoot.host.clientWidth
4432                this.makeHighRes(this.panel);
4433                requestAnimationFrame(this.draw);
4434            }
4435
4436            get data() {
4437                return this._data;
4438            }
4439
4440            set c(value) {
4441                this.historySpan.style.display = 'block';
4442                this._c = value;
4443                this.history.push(this._c)
4444                // 下面代码实现 canvas 随内容高度变化
4445                // this.maxDepth = this._getMaxDepth(value) + 5; //设置最大层级
4446                // this.panel.height = this.maxDepth * this.rowHeight
4447                // this.panel.width = this.shadowRoot.host.clientWidth;
4448                // this.makeHighRes(this.panel);
4449                requestAnimationFrame(this.draw);
4450            }
4451
4452            get c() {
4453                return this._c;
4454            }
4455
4456            zoomOut(value) {
4457                this._c = value;
4458                // 下面代码实现 canvas 随内容高度变化
4459                // this.maxDepth = this._getMaxDepth(value) + 5; //设置最大层级
4460                // // this.sumCount = value.reduce((acc, cur) => acc + cur.s, 0); //设置根节点的 sumCount
4461                // this.panel.height = this.maxDepth * this.rowHeight
4462                // this.panel.width = this.shadowRoot.host.clientWidth;
4463                // this.makeHighRes(this.panel);
4464                requestAnimationFrame(this.draw);
4465            }
4466
4467            makeHighRes(canvas) {
4468                let ctx = canvas.getContext('2d');
4469                let dpr = window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1;
4470                let oldWidth = canvas.width;
4471                let oldHeight = canvas.height;
4472                canvas.width = Math.round(oldWidth * dpr);
4473                canvas.height = Math.round(oldHeight * dpr);
4474                canvas.style.width = oldWidth + 'px';
4475                canvas.style.height = oldHeight + 'px';
4476                ctx.scale(dpr, dpr);
4477                this.context = ctx;
4478                return ctx;
4479            }
4480
4481            draw = () => {
4482                // requestAnimationFrame(this.draw);
4483                let ctx = this.context;
4484                // ctx.clearRect(0, 0, this.panel.width, this.panel.height);
4485                let grad = ctx.createLinearGradient(0, 0, 0, this.panel.height / 2); //创建一个渐变色线性对象
4486                grad.addColorStop(0, "#eeeeee");                  //定义渐变色颜色
4487                grad.addColorStop(1, "#efefb1");
4488                ctx.fillStyle = grad;                         //设置fillStyle为当前的渐变对象
4489                ctx.fillRect(0, 0, this.panel.width, this.panel.height);
4490                if (this.data) {
4491                    if (this.reverse) {
4492                        this.drawCReverse(
4493                            this.c,
4494                            1,
4495                            {
4496                                x: 0,
4497                                y: this.rowHeight * 4,
4498                                w: this.panel.clientWidth,
4499                                h: this.rowHeight
4500                            });
4501                    } else {
4502                        this.drawC(
4503                            this.c,
4504                            1,
4505                            {
4506                                x: 0,
4507                                y: this.panel.clientHeight - this.rowHeight,
4508                                w: this.panel.clientWidth,
4509                                h: this.rowHeight
4510                            });
4511                    }
4512                }
4513            }
4514
4515            getStatistics(c) {
4516                let statistics;//鼠标hover展示的百分比
4517                switch (this.type) {
4518                    case 1: //current thread
4519                        statistics = c.subEvents * 100 / this.eventCountCurrentThread;
4520                        statistics = statistics.toFixed(2)
4521                        statistics = `${statistics}%`
4522                        break;
4523                    case 2: // current process
4524                        statistics = c.subEvents * 100 / this.eventCountCurrentProcess;
4525                        statistics = statistics.toFixed(2)
4526                        statistics = `${statistics}%`
4527                        break;
4528                    case 3: // all process
4529                        statistics = c.subEvents * 100 / this.eventCountAllProcess;
4530                        statistics = statistics.toFixed(2)
4531                        statistics = `${statistics}%`
4532                        break;
4533                    case 4: //event count
4534                        statistics = c.subEvents;
4535                        statistics = `${statistics}`
4536                        break;
4537                    case 5: //event count in milliseconds
4538                        statistics = c.subEvents / 1000000;
4539                        statistics = statistics.toFixed(3)
4540                        statistics = `${statistics} ms`
4541                        break;
4542                    default: //current thread
4543                        statistics = c.subEvents * 100 / this.eventCountCurrentThread;
4544                        statistics = statistics.toFixed(2)
4545                        statistics = `${statistics}%`
4546                        break;
4547                }
4548                return statistics;
4549            }
4550
4551            //HTML反转义
4552            htmlDecode(text) {
4553                let temp = document.createElement("div");
4554                temp.innerHTML = text;
4555                let output = temp.innerText || temp.textContent;
4556                temp = null;
4557                return output;
4558            }
4559
4560            getFunctionName(f) {
4561                let funName = "";
4562                if (this.data.json.SymbolMap[f]) {
4563                    funName = this.data.json.SymbolMap[f].symbol;
4564                } else {
4565                    let f = c[i].symbol;
4566                    console.log(`processId:${this.pid.textContent} processName:${this.processName.textContent} threadId:${this.tid.textContent} threadName:${this.threadName.textContent}`, c[i], "SymbolMap中没有对应的值")
4567                }
4568                return this.htmlDecode(funName);
4569            }
4570
4571            getColor(percent2, funName) {
4572                let heatColor;
4573                if (this.keyword && this.keyword.length > 0 && funName.indexOf(this.keyword) != -1) {
4574                    heatColor = { r: 0x66, g: 0xad, b: 0xff };
4575                } else {
4576                    heatColor = this.getHeatColor(percent2);
4577                }
4578                return heatColor;
4579            }
4580
4581            drawCReverse = (c, dept, rect) => {
4582                let ctx = this.context;
4583                let offset = 0
4584                for (let i = 0; i < c.length; i++) {
4585                    let funName = this.getFunctionName(c[i].symbol);
4586                    let funcId = c[i].symbol;
4587                    let percent = c[i].subEvents * 100 / (c.reduce((acc, cur) => acc + cur.subEvents, 0));
4588                    let percent2 = c[i].subEvents * 100 / this.sumCount;
4589                    if (percent2 < 0.1) continue //过滤掉 百分比为0.1一下的节点
4590                    let heatColor = this.getColor(percent2, funName);
4591                    let w = rect.w * (percent / 100.0);
4592                    if (w < 1) {
4593                        w = 1
4594                    }
4595                    let _x = rect.x + offset;
4596                    //绘制填充矩形
4597                    ctx.fillStyle = `rgba(${heatColor.r}, ${heatColor.g}, ${heatColor.b}, 1)`;
4598                    ctx.fillRect(_x, rect.y + 2, w, rect.h - 2);
4599                    // 绘制文本
4600                    ctx.fillStyle = "rgba(0,0,0,1)";
4601                    let txtWidth = ctx.measureText(funName).width;//文本长度
4602                    let chartWidth = txtWidth / funName.length;//每个字符长度
4603                    let number = (w - 6) / chartWidth;//可以显示多少字符
4604                    if (number >= 4 && number < funName.length - 3) {
4605                        ctx.fillText(funName.slice(0, number - 3) + '...', _x + 3, rect.y + 13, w - 6)
4606                    } else if (number >= 4) {
4607                        ctx.fillText(funName, _x + 3, rect.y + 13, w - 6)
4608                    }
4609                    let _rect = {
4610                        x: _x, y: rect.y, w: w, h: rect.h
4611                    }
4612                    c[i].rect = _rect;
4613
4614                    if (this.mouseX > _x && this.mouseX < _x + w && this.mouseY > rect.h * 3 + (dept) * rect.h && this.mouseY < rect.h * 3 + (dept + 1) * rect.h) {
4615                        if (this.mouseState === 'mouseMove') {
4616                            //绘制边框矩形
4617                            // ctx.font = '12px serif';
4618                            ctx.lineWidth = 2;
4619                            ctx.strokeStyle = `#000000`;
4620                            ctx.strokeRect(_x, rect.y + 1, w - 1, rect.h);
4621                            this.funcNameSpan.textContent = funName
4622                            this.panel.title = funName
4623                            this.percentSpan.textContent = this.getStatistics(c[i]);
4624                        } else {
4625                            if (this.mouseState === 'mouseUp') {
4626                                this.mouseState = null;
4627                                if (!this.compareNodes(this.c, [c[i]])) {
4628                                    this.c = [c[i]];
4629                                }
4630                            }
4631                        }
4632                    } else {
4633                        ctx.lineWidth = 1;
4634                        // ctx.font = '11px serif';
4635                    }
4636                    offset += w;
4637                    // console.log(_rect,dept,percent,funName);
4638                    //递归绘制子节点
4639                    if (c[i].callStack && c[i].callStack.length > 0) {
4640                        _rect.y = _rect.y + _rect.h
4641                        this.drawCReverse(c[i].callStack, dept + 1, _rect);
4642                    }
4643                }
4644            }
4645            drawC = (c, dept, rect) => {
4646                let ctx = this.context;
4647                let offset = 0
4648                for (let i = 0; i < c.length; i++) {
4649                    let funName = this.getFunctionName(c[i].symbol);
4650                    // console.log(dept,this.data.SymbolMap[c[i].symbol].symbol);
4651                    let funcId = c[i].symbol;
4652                    let percent = c[i].subEvents * 100 / (c.reduce((acc, cur) => acc + cur.subEvents, 0));//sumCount;
4653                    let percent2 = c[i].subEvents * 100 / this.sumCount;
4654                    if (percent2 < 0.1) continue //过滤掉 百分比为0.1一下的节点
4655                    let heatColor = this.getColor(percent2, funName);
4656                    let w = rect.w * (percent / 100.0);
4657                    if (w < 1) {
4658                        w = 1
4659                    }
4660                    let _x = rect.x + offset;
4661                    //绘制填充矩形
4662                    ctx.fillStyle = `rgba(${heatColor.r}, ${heatColor.g}, ${heatColor.b}, 1)`;
4663                    ctx.fillRect(_x, rect.y + 2, w, rect.h - 2);
4664                    // 绘制文本
4665                    ctx.fillStyle = "rgba(0,0,0,1)";
4666                    let txtWidth = ctx.measureText(funName).width;//文本长度
4667                    let chartWidth = txtWidth / funName.length;//每个字符长度
4668                    let number = (w - 6) / chartWidth;//可以显示多少字符
4669                    if (number >= 4 && number < funName.length - 3) {
4670                        ctx.fillText(funName.slice(0, number - 3) + '...', _x + 3, rect.y + 13, w - 6)
4671                    } else if (number >= 4) {
4672                        ctx.fillText(funName, _x + 3, rect.y + 13, w - 6)
4673                    }
4674                    let _rect = {
4675                        x: _x, y: rect.y, w: w, h: rect.h
4676                    }
4677                    c[i].rect = _rect;
4678
4679                    if (this.mouseX > _x && this.mouseX < _x + w && this.mouseY > (this.maxDepth - dept) * rect.h && this.mouseY < (this.maxDepth - dept + 1) * rect.h) {
4680                        if (this.mouseState === 'mouseMove') {
4681                            //绘制边框矩形
4682                            // ctx.font = '12px serif';
4683                            ctx.lineWidth = 2;
4684                            ctx.strokeStyle = `#000000`;
4685                            ctx.strokeRect(_x, rect.y + 1, w - 1, rect.h);
4686                            this.funcNameSpan.textContent = funName
4687                            this.panel.title = funName
4688                            this.percentSpan.textContent = this.getStatistics(c[i]);
4689                        } else {
4690                            if (this.mouseState === 'mouseUp') {
4691                                this.mouseState = null;
4692                                if (!this.compareNodes(this.c, [c[i]])) {
4693                                    this.c = [c[i]];
4694                                }
4695                            }
4696                        }
4697                    } else {
4698                        ctx.lineWidth = 1;
4699                        // ctx.font = '11px serif';
4700                    }
4701                    offset += w;
4702                    // console.log(_rect,dept,percent,funName);
4703                    //递归绘制子节点
4704                    if (c[i].callStack && c[i].callStack.length > 0) {
4705                        _rect.y = _rect.y - _rect.h
4706                        this.drawC(c[i].callStack, dept + 1, _rect);
4707                    }
4708                }
4709            }
4710
4711            compareNode(na, nb) {
4712                let res = false;
4713                if (na.selfEvents === nb.selfEvents && na.subEvents === nb.subEvents && na.symbol === nb.symbol) {
4714                    res = this.compareNodes(na.callStack || [], nb.callStack || []);
4715                }
4716                return res;
4717            }
4718
4719            compareNodes(a, b) {
4720                let res = false;
4721                if (a.length === b.length) {
4722                    if (a.length === 0) {
4723                        return true;
4724                    }
4725                    for (let i = 0; i < a.length; i++) {
4726                        res = this.compareNode(a[i], b[i])
4727                    }
4728                }
4729                return res;
4730            }
4731
4732            getMaxDepth(nodes) {
4733                let isArray = Array.isArray(nodes);
4734                let sumCount;
4735                if (isArray) {
4736                    sumCount = nodes.reduce((acc, cur) => acc + cur.subEvents, 0);
4737                } else {
4738                    sumCount = nodes.subEvents;
4739                }
4740                let width = sumCount * 100.0 / this.sumCount;
4741                if (width < 0.1) {
4742                    return 0;
4743                }
4744                let children = isArray ? this.splitChildrenForNodes(nodes) : nodes.callStack;
4745                let childDepth = 0;
4746                if (children) {
4747                    for (let child of children) {
4748                        childDepth = Math.max(childDepth, this.getMaxDepth(child));
4749                    }
4750                }
4751                return childDepth + 1;
4752            }
4753
4754            splitChildrenForNodes(nodes) {
4755                let map = new Map();
4756                for (let node of nodes) {
4757                    for (let child of node.callStack) {
4758                        let subNodes = map.get(child.symbol);
4759                        if (subNodes) {
4760                            subNodes.push(child);
4761                        } else {
4762                            map.set(child.symbol, [child]);
4763                        }
4764                    }
4765                }
4766                let res = [];
4767                for (let subNodes of map.values()) {
4768                    res.push(subNodes.length == 1 ? subNodes[0] : subNodes);
4769                }
4770                return res;
4771            }
4772
4773            getHeatColor(widthPercentage) {
4774                return {
4775                    r: Math.floor(245 + 10 * (1 - widthPercentage * 0.01)),
4776                    g: Math.floor(110 + 105 * (1 - widthPercentage * 0.01)),
4777                    b: 100,
4778                };
4779            }
4780
4781            attributeChangedCallback(name, oldValue, newValue) {
4782            }
4783        }
4784        if (!customElements.get('app-normal-flame')) {
4785            customElements.define('app-normal-flame', AppNormalFlame);
4786        }
4787
4788        class AppFlameGraph extends HTMLElement {
4789            static get observedAttributes() {
4790                return ['color', 'size']
4791            }
4792
4793            constructor() {
4794                super();
4795                const shadowRoot = this.attachShadow({ mode: 'open' });
4796                shadowRoot.innerHTML = `
4797        <style>
4798        :host{
4799            font-size:inherit;
4800            display:inline-flex;
4801            align-items: center;
4802            justify-content:center;
4803            width: 100%;
4804        }
4805        </style>
4806        <div style="width: 100%;display: flex;flex-direction: column">
4807            <lit-select id="typeSelect" default-value="1" mode="single" style="width:40vw;margin-bottom: 10px;align-self: flex-end">
4808                    <lit-select-option value="1">Show percentage of event count relative to the current thread</lit-select-option>
4809                    <lit-select-option value="2">Show percentage of event count relative to the current process</lit-select-option>
4810                    <lit-select-option value="3">Show percentage of event count relative to all process</lit-select-option>
4811                    <lit-select-option value="4">show event count</lit-select-option>
4812                    <lit-select-option value="5">show event count in milliseconds</lit-select-option>
4813            </lit-select>
4814            <div id="panel" style="width: 100%"></div>
4815        </div>
4816        <slot></slot>
4817        `
4818            }
4819
4820            get dataRefer() {
4821                return this._dataRefer;
4822            }
4823
4824            //设置比对的数据,【这里是json文件中加载的数据 需要从 recordSampleInfo[index] 取进程集合 线程集合】
4825            set dataRefer(val) {
4826                this._dataRefer = val;
4827            }
4828
4829            get data() {
4830                return this._json || null;
4831            }
4832
4833            set data(json) {
4834                //如果已经给过值,不重新刷新
4835                if (this.isFinished) {
4836                    return;
4837                }
4838                this._json = json;
4839                this.panel = this.shadowRoot.getElementById('panel');
4840                this.panel.innerHTML = '';
4841                let processes = json.recordSampleInfo[window.eventIndex].processes;
4842                let processesRefer;
4843                if (this.dataRefer) {
4844                    processesRefer = this.dataRefer.recordSampleInfo[window.eventIndex].processes;
4845                }
4846                processes.slice(0).forEach(it => {
4847                    let itRefer
4848                    if (processesRefer) {
4849                        itRefer = processesRefer.find(element => element.pid == it.pid);//找到要比较的进程
4850                    }
4851                    //slice(0,1) 控制只绘制一个线程数据,用作测试
4852                    it.threads.slice(0).forEach(th => {
4853                        let pid = it.pid;
4854                        let processName = json.processNameMap[it.pid];
4855                        let tid = th.tid;
4856                        let threadName = json.threadNameMap[th.tid];
4857                        let eventCount = th.eventCount;
4858                        let sampleCount = th.sampleCount;
4859                        let g = th.CallOrder;
4860                        if (this.dataRefer) {
4861                            console.log("create diff")
4862                            let thRefer;
4863                            if (itRefer) {
4864                                thRefer = itRefer.threads.find(e => e.tid == th.tid);//找到要比较的线程
4865                            }
4866                            let chart = document.createElement('app-diff-flame');
4867                            chart.style.width = '100%'
4868                            chart.style.height = 'auto';
4869                            chart.style.display = 'flex'
4870                            this.panel.appendChild(chart);
4871                            chart.threadRefer = thRefer;//注意 app-flame-graph中的dataRefer中保存的是加载的json完整数据集,app-chart-flame中的dataRefer缓存的是对比的线程数据
4872                            chart.data = {
4873                                json: json,//显示的json数据集【直接从json文件读取的完整json结构】
4874                                jsonRefer: this.dataRefer,//需要把完整的对比数据也传入过去,因为只传入thread对比数据需要 完整数据中的eventCount去计算百分比,计算宽度,然后对比原始宽度多还是少
4875                                type: this.type || 1,
4876                                pid, processName, tid, threadName, eventCount, sampleCount, CallOrder: g
4877                            }
4878                        } else {
4879                            console.log("create normal")
4880                            let chart = document.createElement('app-normal-flame');
4881                            chart.style.width = '100%'
4882                            chart.style.height = 'auto';
4883                            chart.style.display = 'flex'
4884                            this.panel.appendChild(chart);
4885                            chart.data = {
4886                                json: json,
4887                                type: this.type || 1,
4888                                pid, processName, tid, threadName, eventCount, sampleCount, CallOrder: g
4889                            }
4890                        }
4891                        // console.log(pid,processName,tid,threadName,sampleCount,g);
4892                    })
4893                })
4894                this.isFinished = true;
4895            }
4896
4897            connectedCallback() {
4898                this.isFinished = false;
4899                this.panel = this.shadowRoot.getElementById('panel');
4900                this.typeSelect = this.shadowRoot.getElementById('typeSelect');
4901                this.typeSelect.onchange = ev => {
4902                    this.type = parseInt(ev.detail.value);
4903                    this.isFinished = false;
4904                    this.data = this.data; //切换分类后刷新
4905                }
4906            }
4907
4908            attributeChangedCallback(name, oldValue, newValue) {
4909                if (name == 'color' && this.loading) {
4910                    this.loading.style.color = newValue;
4911                }
4912                if (name == 'size' && this.loading) {
4913                    this.loading.style.fontSize = newValue + 'px';
4914                }
4915            }
4916        }
4917        if (!customElements.get('app-flame-graph')) {
4918            customElements.define('app-flame-graph', AppFlameGraph);
4919        }
4920
4921        (function (values) {
4922
4923            function createPromise(callback) {
4924                if (callback) {
4925                    return new Promise((resolve, _) => callback(resolve));
4926                }
4927                return new Promise((resolve, _) => resolve());
4928            }
4929
4930            function initGlobalObjects1() {
4931                let recordData = document.querySelector('#record_data_diff_1').textContent;
4932                if (recordData.trim().length > 0) {
4933                    return new Promise((resolve, reject) => {
4934                        resolve(JSON.parse(recordData));
4935                    })
4936                } else {
4937                    return fetch('data-diff-1.json').then(response => response.json())
4938                }
4939            }
4940            function initGlobalObjects2() {
4941                let recordData = document.querySelector('#record_data_diff_2').textContent;
4942                if (recordData.trim().length > 0) {
4943                    return new Promise((resolve, reject) => {
4944                        resolve(JSON.parse(recordData));
4945                    })
4946                } else {
4947                    return fetch('data-diff-2.json').then(response => response.json())
4948                }
4949            }
4950
4951            function waitDocumentReady() {
4952                return createPromise((resolve) => document.addEventListener("DOMContentLoaded", resolve));
4953            }
4954
4955            createPromise()
4956                .then(waitDocumentReady)
4957                .then(() => Promise.all([initGlobalObjects1(), initGlobalObjects2()]))
4958                .then((array) => {
4959                    let j1 = array[0]
4960                    let j2 = array[1]
4961                    // window.data = j1; // 没有用到function 模块 不用保存
4962                    let eventSelector1 = document.querySelector('#events1')
4963                    let changeBaseBt = document.querySelector('#changeBaseBt')
4964                    if (j1.recordSampleInfo && j1.recordSampleInfo.length > 0) {
4965                        let events = [];
4966                        j1.recordSampleInfo.forEach((e, index) => {
4967                            events.push({ key: index + '', val: e.eventConfigName })
4968                        })
4969                        eventSelector1.dataSource = events;
4970                        window.eventIndex = 0;
4971                    }
4972                    let loading = document.querySelector('#loading');
4973                    let tabs = document.querySelector('#tabs')
4974                    let diffFlame = document.querySelector('#diff-flame');
4975                    let flame1 = document.querySelector('#flame-1');
4976                    let flame2 = document.querySelector('#flame-2');
4977                    let legend1 = document.querySelector('#legend-1');
4978                    let legend2 = document.querySelector('#legend-2');
4979                    tabs.onTabClick = (e) => {
4980                        if (e.detail.key == 1) {
4981                            diffFlame.isFinished = false;
4982                            diffFlame.dataRefer = window.isReserved ? j1 : j2; //对换显示数据和对比数据
4983                            diffFlame.data = window.isReserved ? j2 : j1;//将 对比的数据当作显示数据
4984                        } else if (e.detail.key == 2) {
4985                            flame1.isFinished = false;
4986                            flame1.data = j1;
4987                        } else {
4988                            flame2.isFinished = false;
4989                            flame2.data = j2;
4990                        }
4991                    }
4992                    //火焰图
4993                    window.isReserved = false; //定义变量。表示是否反转基本画图数据,默认为false ,表示以 data-diff-1.json 数据图形数据,data-diff-2.json 为比较数据
4994                    diffFlame.isFinished = false;
4995                    diffFlame.dataRefer = j2; //设置需要对比的数据集,flame.data赋值会引起绘制,所以这行要先设置对比数据
4996                    diffFlame.data = j1;//要绘制火焰图需要显示的数据
4997                    eventSelector1.addEventListener('change', (e) => {
4998                        loading.style.display = 'flex'
4999                        window.eventIndex = parseInt(e.detail.value);//改变界面最上面的select 缓存的index刷新 绘制的数据需要这个index去总数据中取
5000                        diffFlame.isFinished = false;
5001                        diffFlame.dataRefer = j2;
5002                        diffFlame.data = j1;//这里触发了界面更新
5003                        flame1.isFinished = false;
5004                        flame1.data = j1;
5005                        flame2.isFinished = false;
5006                        flame2.data = j2;
5007                        loading.style.display = 'none'
5008                    })
5009                    changeBaseBt.addEventListener('click', (e) => {
5010                        window.isReserved = !window.isReserved
5011                        changeBaseBt.setAttribute('enable', 'false');
5012                        loading.style.display = 'flex'
5013                        legend1.textContent = window.isReserved ? "diff-data-2" : "diff-data-1";
5014                        legend2.textContent = window.isReserved ? "diff-data-1" : "diff-data-2";
5015                        diffFlame.isFinished = false;
5016                        diffFlame.dataRefer = window.isReserved ? j1 : j2; //对换显示数据和对比数据
5017                        diffFlame.data = window.isReserved ? j2 : j1;//将 对比的数据当作显示数据
5018                        loading.style.display = 'none'
5019                        changeBaseBt.setAttribute('enable', 'true');
5020                    })
5021                })
5022        }())
5023    </script>
5024    <div style="width: 100%;height: 100%">
5025        <div style="width: 100%;display: flex;flex-direction: column;align-items: center">
5026            <lit-loading id="loading" size="32" style="display: none"></lit-loading>
5027        </div>
5028        <div style="display: flex;flex-direction: row;align-items: center;padding: 15px;justify-content: space-between">
5029            <div>
5030                <span style="font-weight: bold;margin-right: 10px">Event Type :</span>
5031                <lit-select id="events1" default-value="0" style="width: 400px"></lit-select>
5032            </div>
5033        </div>
5034        <lit-tabs id='tabs' position="top-left" activekey="1" mode="flat">
5035            <lit-tabpane id="pane1" tab="diff-flame" key="1">
5036                <div style="display: flex;flex-direction: row;align-items: center;justify-content: space-between">
5037                    <div style="display: flex;flex-direction: row;align-items: center">
5038                        <div style="display: flex;flex-direction: row;align-items: center">
5039                            <span>图形数据: </span>
5040                            <span id="legend-1">data-diff-1</span>
5041                        </div>
5042                        <div style="display: flex;flex-direction: row;margin-left: 20px;align-items: center">
5043                            <span>比较数据: </span>
5044                            <span id="legend-2">data-diff-2</span>
5045                        </div>
5046                        <div id="changeBaseBt" style="background-color: coral;color: white;margin-left: 40px;
5047                padding: 7px 15px 7px 15px;border-radius: 5px;cursor: pointer">change</div>
5048                    </div>
5049                    <div style="display: flex;flex-direction: row;align-items: center">
5050                        <div style="display: flex;flex-direction: row;align-items: center">
5051                            <span style="width: 10px;height: 10px;background-color:#2894FF;margin-right: 10px"></span>
5052                            <span>函数执行时间相比小于(颜色越深则差值越大)</span>
5053                        </div>
5054                        <div style="display: flex;flex-direction: row;align-items: center;margin-left: 30px">
5055                            <span style="width: 10px;height: 10px;background-color:#FF2D2D;margin-right: 10px"></span>
5056                            <span>函数执行时间相比大于(颜色越深则差值越大)</span>
5057                        </div>
5058                    </div>
5059                </div>
5060                <div style="padding: 20px">
5061                    <app-flame-graph id="diff-flame"></app-flame-graph>
5062                </div>
5063            </lit-tabpane>
5064            <lit-tabpane id="pane2" tab="data-1-flame" key="2">
5065                <app-flame-graph id="flame-1"></app-flame-graph>
5066            </lit-tabpane>
5067            <lit-tabpane id="pane3" tab="data-2-flame" key="3">
5068                <app-flame-graph id="flame-2"></app-flame-graph>
5069            </lit-tabpane>
5070        </lit-tabs>
5071    </div>
5072