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