<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Report-Diff</title>
<style>
* {
box-sizing: border-box;
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
lit-tabs {
padding-top: 10px;
}
lit-tabpane {
padding: 20px;
}
</style>
</head>
<body>
<script type="module">
window.getShadowRoot = (el) => {
if (el.parentNode) {
return window.getShadowRoot(el.parentNode)
} else {
return el;
}
};
window.getShadowElement = (el) => {
if (el.parentElement) {
return window.getShadowElement(el.parentElement)
} else {
return el;
}
};
class LitIcon extends HTMLElement {
static get observedAttributes() {
return ["name", "size", "color", "path"]
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
font-size: inherit;
display: inline-block;
transition: .3s;
}
:host([spin]){
animation: rotate 1.75s linear infinite;
}
@keyframes rotate {
to{
transform: rotate(360deg);
}
}
.icon{
display: block;
width: 1em;
height: 1em;
margin: auto;
fill: currentColor;
overflow: hidden;
}
</style>
<svg style="display: none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<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>
<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>
<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>
<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>
<!-- <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>-->
<!-- <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>-->
</svg>
<svg class="icon" id="icon" aria-hidden="true" viewBox="0 0 ${this.view} ${this.view}">
${this.path ? '<path id="path"></path>' : '<use id="use"></use>'}
</svg>
`
}
get view() {
return this.getAttribute("view") || 1024;
}
get name() {
return this.getAttribute("name");
}
get path() {
return this.getAttribute("path");
}
set name(value) {
this.setAttribute("name", value);
}
set path(value) {
this.setAttribute("path", value);
}
get size() {
return this.getAttribute("size") || "";
}
get color() {
return this.getAttribute("color") || "";
}
set size(value) {
this.setAttribute("size", value);
}
set color(value) {
this.setAttribute("color", value);
}
get spin() {
return this.hasAttribute('spin');
}
set spin(value) {
if (value) {
this.setAttribute('spin', '')
} else {
this.removeAttribute('spin');
}
}
connectedCallback() {
this.icon = this.shadowRoot.getElementById("icon");
this.use = this.shadowRoot.querySelector("use")
this.d = this.shadowRoot.querySelector("path");
this.size && (this.size = this.size);
this.color && (this.color = this.color);
this.name && (this.name = this.name);
this.path && (this.path = this.path);
}
disconnectedCallback() {
}
adoptedCallback() {
console.log('Custom square element moved to new page.');
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === "name" && this.use) {
this.use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', `#icon-${newValue}`);
}
if (name === "path" && this.d) {
this.d.setAttribute("d", newValue);
}
if (name === "color" && this.icon) {
this.icon.style.color = newValue;
}
if (name === "size" && this.icon) {
this.icon.style.fontSize = newValue + "px";
}
}
}
if (!customElements.get('lit-icon')) {
customElements.define('lit-icon', LitIcon);
}
class LitInput extends HTMLElement {
static get observedAttributes() {
return [
'placeholder',
'block',
'icon',
'bordered',
'allow-clear',
'rows',
'maxlength',
'type',
'pattern',
'error-text',
'error-placement',
'required',
'value',
];
}
get value() {
return this.getAttribute('value') || '';
}
set value(value) {
this.setAttribute('value', value);
}
get required() {
return this.hasAttribute('required');
}
set required(value) {
if (value) {
this.setAttribute('required', "");
} else {
this.removeAttribute('required');
}
}
get pattern() {
return this.getAttribute('pattern') || '';
}
set pattern(value) {
this.setAttribute('pattern', value);
}
get errorText() {
return this.getAttribute('error-text') || '该项为必填项';
}
set errorText(value) {
this.setAttribute('error-text', value)
}
get errorPlacement() {
return this.getAttribute('error-placement') || 'top';
}
set errorPlacement(value) {
this.setAttribute('error-placement', value)
}
get type() {
return this.getAttribute('type');
}
set type(value) {
this.setAttribute('type', value);
}
get maxlength() {
return this.getAttribute('maxlength') || '';
}
set maxlength(value) {
this.setAttribute('maxlength', value)
}
get rows() {
return this.getAttribute('rows');
}
set rows(value) {
this.setAttribute('rows', value);
}
get allowClear() {
return this.hasAttribute('allow-clear');
}
set allowClear(value) {
if (value) {
this.setAttribute('allow-clear', '');
} else {
this.removeAttribute('allow-clear');
}
}
get bordered() {
return this.getAttribute('bordered') || "true";
}
set bordered(value) {
if (value) {
this.setAttribute('bordered', 'true');
} else {
this.setAttribute('bordered', 'false')
}
}
get icon() {
return this.getAttribute('icon') || null;
}
set icon(value) {
this.setAttribute('icon', value)
}
get block() {
return this.hasAttribute('block')
}
set block(value) {
if (value) {
this.setAttribute('block', '');
} else {
this.removeAttribute('block')
}
}
get placeholder() {
return this.getAttribute('placeholder') || '';
}
set placeholder(value) {
this.setAttribute('placeholder', value);
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
${this.bordered === 'true' ? 'border: 1px solid #e9e9e9;' : ''}
display: inline-flex;
box-sizing: border-box;
position:relative;
align-items: ${this.rows ? 'flex-start' : 'center'};
transition: all .3s;
border-radius: 2px;
font-size: 14px;
color: #333;
${this.block ? 'display: flex;' : ''}
box-sizing: border-box;
}
:host(:hover){
${this.bordered === 'true' ? 'border: 1px solid #65b687;' : ''}
${this.bordered === 'true' ? 'box-shadow: 0 0 10px #65b68766;' : ''}
}
*{
box-sizing: border-box;
}
.input{
outline: none;
border: 0px;
font-size: inherit;
color: inherit;
width: 100%;
height: 100%;
vertical-align:middle;
line-height:inherit;
height:inherit;
padding: 6px 6px 6px ${this.icon ? '6px' : '11px'};
max-height: inherit;
box-sizing: border-box;
}
/*去掉type=number 后面的增减箭头*/
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
input[type='number'] {
-moz-appearance: textfield;
}
lit-tips{
flex: 1;
height: 100%;
width: 100%;
}
lit-tips{
flex: 1;
align-items: center;
}
:host(:not([allow-clear])) .clear-btn{
display: flex;
visibility: hidden;
transition: all .3s;
opacity: 0;
}
:host([allow-clear]) .clear-btn{
opacity: 0;
position:absolute;
right: 0;
top: .5rem;
visibility: hidden;
transition: all .3s;
display: flex;
margin-right: 6px;
transform: translateY(0%);
color: #bfbfbf;
}
:host([allow-clear]) .clear-btn:hover{
color: #8c8c8c;
}
.area{
outline: none;
border: 0px;
width: 100%;
font-size: inherit;
vertical-align:middle;
min-height: calc(2rem);
max-height: calc(${this.rows} * 1rem);
padding: 6px 6px 6px ${this.icon ? '6px' : '11px'};
box-sizing: border-box;
resize:vertical;
}
:host(:not([type=password])) .pwd-btn,
:host(:not([type=tel])) .pwd-btn{
display: none;
visibility: hidden;
transition: all .3s;
opacity: 0;
}
:host([type=password]) .pwd-btn,
:host([type=tel]) .pwd-btn{
opacity: 1;
position:absolute;
right: 0;
top: .5rem;
visibility: visible;
transition: all .3s;
display: flex;
margin-right: 6px;
transform: translateY(0%);
color: #bfbfbf;
}
/*:host([type=password]) input{*/
/* -webkit-text-security:none;*/
/* text-security:none;*/
/*}*/
:host([type=password]) .pwd-btn:hover,
:host([type=tel]) .pwd-btn:hover{
color: #8c8c8c;
}
</style>
<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>
<lit-tips tips="${this.errorText}" placement="${this.errorPlacement}" color="#f4615c" offset="12px" show="false">
${this.hasAttribute('rows') ?
`<textarea class="area" rows="${this.rows}" value="${this.value}" placeholder="${this.placeholder}" cols="27" maxlength="${this.maxlength}" onscroll="this.rows++;"></textarea>`
:
`<input type="${this.type}" value="${this.value}" class="input" placeholder="${this.placeholder}" >`}
<lit-icon class="clear-btn" name="close-circle-fill"></lit-icon>
<lit-icon class="pwd-btn" name="eye-fill"></lit-icon>
</lit-tips>
<slot name="suffix" ></slot>
`
}
connectedCallback() {
this.reg = new RegExp(this.pattern);
this.inputElement = this.shadowRoot.querySelector('.input');
this.areaElement = this.shadowRoot.querySelector('.area');
this.clearElement = this.shadowRoot.querySelector('.clear-btn');
this.pwdElement = this.shadowRoot.querySelector('.pwd-btn');
this.pwdElement.onclick = (e) => {
if (this.inputElement.getAttribute('type') === 'password') {
this.pwdElement.name = 'eyeclose-fill';
this.inputElement.setAttribute('type', 'tel');
} else if (this.inputElement.getAttribute('type') === 'tel') {
this.inputElement.setAttribute('type', 'password');
this.pwdElement.name = 'eye-fill';
}
}
if (this.areaElement) {
this.clearElement.onclick = e => {
this.areaElement.value = '';
this.clearElement.style.visibility = 'hidden';
this.clearElement.style.opacity = '0';
this.value = this.areaElement.value;
this.dispatchEvent(new CustomEvent('onClear', e));
this.clearError();
}
this.areaElement.oninput = e => {
if (this.allowClear) {
if (this.areaElement.value.length > 0) {
this.clearElement.style.visibility = 'visible';
this.clearElement.style.opacity = '1';
} else {
this.clearElement.style.visibility = 'hidden';
this.clearElement.style.opacity = '0';
}
}
this.value = this.areaElement.value;
this.verify();
this.dispatchEvent(new CustomEvent('input', e));
}
this.areaElement.onchange = e => {
this.value = this.areaElement.value;
this.verify();
this.dispatchEvent(new CustomEvent('change', e));
}
this.areaElement.onfocus = e => {
this.value = this.areaElement.value;
this.verify();
this.dispatchEvent(new CustomEvent('focus', e));
}
this.areaElement.onblur = e => {
this.value = this.areaElement.value;
this.dispatchEvent(new CustomEvent('blur', e));
}
this.areaElement.onkeydown = e => {
if (e.key === 'Enter') {
this.value = this.areaElement.value;
this.verify();
this.dispatchEvent(new CustomEvent('onPressEnter', e));
}
this.dispatchEvent(new CustomEvent('keydown', e));
}
this.areaElement.onkeyup = e => {
this.dispatchEvent(new CustomEvent('keyup', e));
}
this.areaElement.onselect = e => {
this.dispatchEvent(new CustomEvent('select', e));
}
this.areaElement.onclick = e => {
this.dispatchEvent(new CustomEvent('click', e));
}
}
if (this.inputElement) {
this.clearElement.onclick = e => {
this.inputElement.value = '';
this.clearElement.style.visibility = 'hidden';
this.clearElement.style.opacity = '0';
this.value = this.inputElement.value;
this.dispatchEvent(new CustomEvent('onClear', e));
this.clearError();
}
this.inputElement.oninput = e => {
if (this.allowClear) {
if (this.inputElement.value.length > 0) {
this.clearElement.style.visibility = 'visible';
this.clearElement.style.opacity = '1';
} else {
this.clearElement.style.visibility = 'hidden';
this.clearElement.style.opacity = '0';
}
}
this.value = this.inputElement.value;
this.verify();
this.dispatchEvent(new CustomEvent('input', e));
}
this.inputElement.onchange = e => {
this.value = this.inputElement.value;
this.verify();
this.dispatchEvent(new CustomEvent('change', e));
}
this.inputElement.onfocus = e => {
this.value = this.inputElement.value;
this.verify();
this.dispatchEvent(new CustomEvent('focus', e));
}
this.inputElement.onblur = e => {
this.dispatchEvent(new CustomEvent('blur', e));
}
this.inputElement.onkeydown = e => {
if (e.key === 'Enter') {
this.value = this.inputElement.value;
this.verify();
this.dispatchEvent(new CustomEvent('onPressEnter', e));
}
this.dispatchEvent(new CustomEvent('keydown', e));
}
this.inputElement.onkeyup = e => {
this.dispatchEvent(new CustomEvent('keyup', e));
}
this.inputElement.onselect = e => {
this.dispatchEvent(new CustomEvent('select', e));
}
this.inputElement.onclick = e => {
this.dispatchEvent(new CustomEvent('click', e));
}
}
}
disconnectedCallback() {
}
adoptedCallback() {
console.log('Custom square element moved to new page.');
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'value') {
if (this.inputElement) {
this.inputElement.value = newValue
} else if (this.areaElement) {
this.areaElement.value = newValue;
}
}
}
verify() {
if (this.required) {
let result;
if (this.hasAttribute('rows')) {
result = this.reg.test(this.areaElement.value);
} else {
result = this.reg.test(this.inputElement.value);
}
if (result) {
this.shadowRoot.querySelector('lit-tips').show = 'false'
} else {
this.shadowRoot.querySelector('lit-tips').show = 'true'
}
return result;
} else {
return true;
}
}
clearError() {
this.shadowRoot.querySelector('lit-tips').show = 'false'
}
}
if (!customElements.get('lit-input')) {
customElements.define('lit-input', LitInput);
}
class LitLoading extends HTMLElement {
static get observedAttributes() { return ['color', 'size'] }
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
font-size:inherit;
display:inline-flex;
align-items: center;
justify-content:center;
color:var(--themeColor,#42b983);
}
.loading{
display: block;
width: 1em;
height: 1em;
margin: auto;
animation: rotate 1.4s linear infinite;
}
.circle {
stroke: currentColor;
animation: progress 1.4s ease-in-out infinite;
stroke-dasharray: 80px, 200px;
stroke-dashoffset: 0px;
transition:.3s;
}
:host(:not(:empty)) .loading{
margin:.5em;
}
@keyframes rotate{
to{
transform: rotate(360deg);
}
}
@keyframes progress {
0% {
stroke-dasharray: 1px, 200px;
stroke-dashoffset: 0px;
}
50% {
stroke-dasharray: 100px, 200px;
stroke-dashoffset: -15px;
}
100% {
stroke-dasharray: 100px, 200px;
stroke-dashoffset: -125px;
}
}
</style>
<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>
<slot></slot>
`
}
get size() {
return this.getAttribute('size') || '';
}
get color() {
return this.getAttribute('color') || '';
}
set size(value) {
this.setAttribute('size', value);
}
set color(value) {
this.setAttribute('color', value);
}
connectedCallback() {
this.loading = this.shadowRoot.getElementById('loading');
this.size && (this.size = this.size);
this.color && (this.color = this.color);
}
attributeChangedCallback(name, oldValue, newValue) {
if (name == 'color' && this.loading) {
this.loading.style.color = newValue;
}
if (name == 'size' && this.loading) {
this.loading.style.fontSize = newValue + 'px';
}
}
}
if (!customElements.get('lit-loading')) {
customElements.define('lit-loading', LitLoading);
}
class LitPagination extends HTMLElement {
static get observedAttributes() {
return [
"current",
"total",
"page-size",
"disabled",
'show-size-changer',
'show-quick-jumper',
'page-size-options',
'simple',
]
}
get pageSizeOptions() {
return this.getAttribute('page-size-options');
}
set pageSizeOptions(value) {
this.setAttribute('page-size-options', '');
}
get current() {
return this.getAttribute('current') || '1';
}
set current(value) {
this.setAttribute('current', value);
}
get total() {
return this.getAttribute('total') || '50';
}
set total(value) {
this.setAttribute('total', value);
}
get pageSize() {
return this.getAttribute('page-size') || '10';
}
set pageSize(value) {
this.setAttribute('page-size', value);
}
get disabled() {
return this.hasAttribute('disabled');
}
set disabled(value) {
if (value) {
this.setAttribute('disabled', '');
} else {
this.removeAttribute('disabled');
}
}
get showSizeChanger() {
return this.hasAttribute('show-size-changer')
}
set showSizeChanger(value) {
if (value) {
this.setAttribute('show-size-changer', '');
} else {
this.removeAttribute('show-size-changer');
}
}
get showQuickJumper() {
return this.hasAttribute('show-quick-jumper')
}
set showQuickJumper(value) {
if (value) {
this.setAttribute('show-quick-jumper', '');
} else {
this.removeAttribute('show-quick-jumper');
}
}
get simple() {
return this.hasAttribute('simple');
}
set simple(value) {
if (value) {
this.setAttribute('simple', '');
} else {
this.removeAttribute('simple');
}
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
display: flex;
width: max-content;
}
.root{
display: inline-flex;
/*grid-template-columns: repeat(auto-fill,35px);*/
/*column-gap: 8px;*/
/*row-gap: 8px;*/
user-select: none;
}
.item{
box-sizing: border-box;
color: #333;
transition: all .3s;
width: 35px;
height: 35px;
margin-left: 6px;
padding: 6px 10px;
border: 1px solid #ececec;
border-radius: 3px;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
}
.item-dir{
color: #333;
transition: all .3s;
padding: 5px 10px;
/*border: 1px solid #ececec;*/
/*border-radius: 3px;*/
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
}
.item:not([disable]):hover{
color:#42b983;
border: 1px solid #42b983;
}
.item[selected]{
color:#42b983;
border: 1px solid #42b983;
}
.item[disable]{
/*background-color: #f5f5f5;*/
fill: #c7c7c7;
color: #c7c7c7;
cursor: not-allowed;
}
:host([show-quick-jumper]) .jump-root{
grid-column: span 4;
margin-left: 6px;
display: inline-flex;
align-items: center;
}
:host(:not([show-quick-jumper])) .jump-root{
display: none;
}
:host([show-size-changer]) .pages-change{
display: inline-flex;
grid-column: span 3;
width: 120px;
margin-left: 6px;
font-size: .9rem;
}
:host(:not([show-size-changer])) .pages-change{
display: none;
}
</style>
<div style="display: grid;grid-template-columns: max-content 1fr;">
<div style="display: inline-flex;align-items: center" id="showTotal">
<slot id="st" name="showTotal"></slot>
</div>
<div class="root">
<!-- <lit-icon class="item left-arrow" name="left" ></lit-icon>-->
<svg class="item left-arrow" viewBox="0 0 1024 1024" aria-hidden="true" >
<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>
</svg>
<div class="item first">1</div>
<lit-icon class="item-dir double-left" name="ellipsis" ></lit-icon>
<!-- <svg class="item-dir double-left" viewBox="0 0 1024 1024" aria-hidden="true" style="width: 16px;width: 16px;">-->
<!-- <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>-->
<!-- </svg>-->
<div class="item one">2</div>
<div class="item two">3</div>
<div class="item three">4</div>
<div class="item four">5</div>
<div class="item five">6</div>
<lit-icon class="item-dir double-right" name="ellipsis" ></lit-icon>
<!-- <svg class="item-dir double-right" viewBox="0 0 1024 1024" aria-hidden="true" style="width: 16px;width: 16px;">-->
<!-- <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>-->
<!-- </svg>-->
<div class="item last">1</div>
<!-- <lit-icon class="item right-arrow" name="right"></lit-icon>-->
<svg class="item right-arrow" viewBox="0 0 1024 1024" aria-hidden="true" >
<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>
</svg>
<lit-select class="pages-change" default-value="10"></lit-select>
<div class="jump-root">
<label style="font-size: .9rem;color: #333;margin-right: 5px">跳至</label>
<lit-input type="number" class="jump" style="width: 80px;height: 100%"></lit-input>
<label style="font-size: .9rem;color: #333;margin-left: 5px">页</label>
</div>
</div>
</div>
`
}
connectedCallback() {
this.slotShowTotal = null;
this.rootElement = this.shadowRoot.querySelector('.root');
let st = this.shadowRoot.querySelector('#st');
st.addEventListener('slotchange', () => {
let elements = st.assignedElements();
if (elements.length > 0) {
this.slotShowTotal = elements[0];
}
if (this.slotShowTotal) {
let cloneNode = this.slotShowTotal.render({
total: this._total,
range: [1, this._pages],
}).content.cloneNode(true);
this.shadowRoot.querySelector('#showTotal').append(cloneNode);
}
})
this.leftArrow = this.shadowRoot.querySelector('.left-arrow');
this.first = this.shadowRoot.querySelector('.first');
this.doubleLeft = this.shadowRoot.querySelector('.double-left');
this.one = this.shadowRoot.querySelector('.one');
this.two = this.shadowRoot.querySelector('.two');
this.three = this.shadowRoot.querySelector('.three');
this.four = this.shadowRoot.querySelector('.four');
this.five = this.shadowRoot.querySelector('.five');
this.doubleRight = this.shadowRoot.querySelector('.double-right');
this.last = this.shadowRoot.querySelector('.last');
this.rightArrow = this.shadowRoot.querySelector('.right-arrow');
this.pagesChange = this.shadowRoot.querySelector('.pages-change');
this.jump = this.shadowRoot.querySelector('.jump');
this.nodes = [this.one, this.two, this.three, this.four, this.five]
let jsonArray = JSON.parse(this.pageSizeOptions);
if (jsonArray) {
this.pagesChange.dataSource = jsonArray.map(a => {
return { key: a, val: `${a} 条/页`, }
});
this.pagesChange.value = jsonArray[0] + '';
this.pageSize = jsonArray[0] + '';
} else {
this.pagesChange.dataSource = [10, 20, 50, 100].map(a => {
return { key: a, val: `${a} 条/页`, }
});
this.pagesChange.value = '10';
this.pageSize = '10';
}
this.jump.addEventListener('onPressEnter', e => {
if (e.target.value) {
this.current = e.target.value;
e.target.value = '';
this.match();
}
});
this.pagesChange.onchange = e => {
this.pageSize = e.detail.value;
this.match();
if (this.slotShowTotal) {
let cloneNode = this.slotShowTotal.render({
total: this._total,
range: [1, this._pages],
}).content.cloneNode(true);
this.shadowRoot.querySelector('#showTotal').lastElementChild.remove();
this.shadowRoot.querySelector('#showTotal').append(cloneNode);
}
this.dispatchEvent(new CustomEvent('onShowSizeChange', {
detail: {
current: parseInt(this.current),
size: parseInt(this.pageSize)
}
}))
}
this.leftArrow.onclick = (e) => {
if (this._current > 1) {
this.current = `${this._current - 1}`
this.match();
}
}
this.rightArrow.onclick = (e) => {
if (this._current < this._pages) {
this.current = `${this._current + 1}`
this.match();
}
}
let nodeClickHandler = e => {
this.current = `${e.currentTarget.val}`;
this.match();
};
this.first.onclick = nodeClickHandler;
this.nodes.forEach(a => a.onclick = nodeClickHandler);
this.last.onclick = nodeClickHandler;
this.doubleLeft.onmouseover = e => {
e.target.name = 'doubleleft';
}
this.doubleLeft.onmouseout = e => {
e.target.name = 'ellipsis'
}
this.doubleLeft.onclick = e => {
let k = this._current - 5;
if (k < 1) k = 1;
this.current = `${k}`;
this.match();
}
this.doubleRight.onmouseover = e => {
e.target.name = 'doubleright';
}
this.doubleRight.onmouseout = e => {
e.target.name = 'ellipsis'
}
this.doubleRight.onclick = e => {
let k = this._current + 5;
if (k > this._pages) k = this._pages;
this.current = `${k}`;
this.match();
}
this.match();
}
match() {
this._current = parseInt(this.current);
this._total = parseInt(this.total);
this._pageSize = parseInt(this.pageSize);
this._pages = Math.ceil(this._total / this._pageSize)
if (this._current > this._pages) {
this._current = this._pages;
this.current = `${this._pages}`;
} else if (this._current < 1) {
this._current = 1;
this.current = `1`;
}
if (this._current === 1) {
this.leftArrow.setAttribute('disable', '');
} else {
this.leftArrow.removeAttribute('disable');
}
if (this._current === this._pages) {
this.rightArrow.setAttribute('disable', '');
} else {
this.rightArrow.removeAttribute('disable');
}
this.hiddenNode(this.first, this.last, this.one, this.two, this.three, this.four, this.five, this.doubleLeft, this.doubleRight)
if (this._pages <= 5) {
for (let i = 0; i < this._pages; i++) {
this.displayNode(this.nodes[i]);
this.item(this.nodes[i], i + 1);
}
} else if (this._pages === 6) {
this.displayNode(this.first);
this.item(this.first, 1);
for (let i = 0; i < this._pages; i++) {
this.displayNode(this.nodes[i]);
this.item(this.nodes[i], i + 2);
}
} else {
this.displayNode(this.one, this.two, this.three, this.four, this.five)
if (this._current - 3 > 1 && this._current + 3 < this._pages) {
this.displayNode(this.first, this.last, this.doubleLeft, this.doubleRight);
this.item(this.first, 1);
this.item(this.last, this._pages);
this.item(this.one, this._current - 2);
this.item(this.two, this._current - 1);
this.item(this.three, this._current);
this.item(this.four, this._current + 1);
this.item(this.five, this._current + 2);
} else if (this._current - 3 === 1 && this._current + 3 < this._pages) {
this.displayNode(this.first, this.last, this.doubleRight);
this.item(this.first, 1)
this.item(this.last, this._pages)
for (let i = 0; i < this.nodes.length; i++) {
this.item(this.nodes[i], i + 2);
}
} else if (this._current - 3 < 1 && this._current + 3 < this._pages) {
this.displayNode(this.last, this.doubleRight);
this.item(this.last, this._pages)
for (let i = 0; i < this.nodes.length; i++) {
this.item(this.nodes[i], i + 1);
}
} else if (this._current - 3 > 1 && this._current + 3 === this._pages) {
this.displayNode(this.first, this.last, this.doubleLeft);
this.item(this.first, 1);
this.item(this.last, this._pages);
this.item(this.nodes[0], this._pages - 5);
this.item(this.nodes[1], this._pages - 4);
this.item(this.nodes[2], this._pages - 3);
this.item(this.nodes[3], this._pages - 2);
this.item(this.nodes[4], this._pages - 1);
} else if (this._current - 3 === 1 && this._current + 3 === this._pages) {
this.displayNode(this.first, this.last);
this.item(this.first, 1);
for (let i = 0; i < this._pages; i++) {
this.displayNode(this.nodes[i]);
this.item(this.nodes[i], i + 2);
}
this.item(this.last, 7);
} else if (this._current - 3 < 1 && this._current + 3 === this._pages) {
this.displayNode(this.last);
this.item(this.last, this._pages)
for (let i = 0; i < this.nodes.length; i++) {
this.item(this.nodes[i], i + 1);
}
} else if (this._current - 3 > 1 && this._current + 3 > this._pages) {
this.displayNode(this.first, this.doubleLeft)
this.item(this.first, 1);
this.item(this.nodes[0], this._pages - 4);
this.item(this.nodes[1], this._pages - 3);
this.item(this.nodes[2], this._pages - 2);
this.item(this.nodes[3], this._pages - 1);
this.item(this.nodes[4], this._pages - 0);
} else if (this._current - 3 === 1 && this._current + 3 > this._pages) {
this.displayNode(this.first);
this.item(this.first, 1);
this.item(this.nodes[0], this._pages - 4);
this.item(this.nodes[1], this._pages - 3);
this.item(this.nodes[2], this._pages - 2);
this.item(this.nodes[3], this._pages - 1);
this.item(this.nodes[4], this._pages - 0);
} else if (this._current - 3 < 1 && this._current + 3 > this._pages) {
}
}
}
item(el, val) {
el.val = val;
el.textContent = val;
if (val === this._current) {
el.setAttribute('selected', '');
} else {
el.removeAttribute('selected');
}
}
displayNode() {
[...arguments].forEach(a => a.style.display = 'flex');
}
hiddenNode(n) {
[...arguments].forEach(a => a.style.display = 'none');
}
disconnectedCallback() {
}
adoptedCallback() {
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'current') {
this.dispatchEvent(new CustomEvent('onChange', {
detail: {
page: parseInt(newValue),
pageSize: this._pageSize
}
}));
} else if (name === 'total') {
this.dispatchEvent(new CustomEvent('onChange', {
detail: {
total: parseInt(newValue),
current: this._current,
pageSize: this._pageSize
}
}));
this.match()
}
}
}
if (!customElements.get('lit-pagination')) {
customElements.define('lit-pagination', LitPagination);
}
class LitSelect extends HTMLElement {
static get observedAttributes() {
return [
'value',
'default-value',
'placeholder',
'disabled',
'loading',
'allow-clear',
'show-search',
'list-height',
'border',
'mode',
];
}
get value() {
return this.getAttribute('value') || this.defaultValue;
}
set value(value) {
this.setAttribute('value', value);
}
get border() {
return this.getAttribute('border') || 'true';
}
set border(value) {
if (value) {
this.setAttribute('border', 'true');
} else {
this.setAttribute('border', 'false');
}
}
get listHeight() {
return this.getAttribute('list-height') || '256px';
}
set listHeight(value) {
this.setAttribute('list-height', value);
}
get defaultPlaceholder() {
return this.getAttribute('placeholder') || '请选择';
}
get showSearch() {
return this.hasAttribute('show-search');
}
set defaultValue(value) {
this.setAttribute('default-value', value);
}
get defaultValue() {
return this.getAttribute('default-value') || '';
}
set placeholder(value) {
this.setAttribute('placeholder', value);
}
get placeholder() {
return this.getAttribute('placeholder') || this.defaultPlaceholder;
}
get loading() {
return this.hasAttribute('loading');
}
set loading(value) {
if (value) {
this.setAttribute('loading', '');
} else {
this.removeAttribute('loading')
}
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
display: inline-flex;
position: relative;
overflow: visible;
cursor: pointer;
transition: all .3s;
border-radius: 2px;
outline: none;
-webkit-user-select:none ;
-moz-user-select:none;
user-select:none;
/*width: 100%;*/
}
:host(:not([border])),
:host([border='true']){
border: 1px solid #dcdcdc;
}
input{
border: 0;
outline: none;
background-color: transparent;
cursor: pointer;
transition: all .3s;
-webkit-user-select:none ;
-moz-user-select:none;
user-select:none;
display: inline-flex;
}
:host(:not([mode])) input{
width: 100%;
}
:host([mode]) input{
padding: 6px 0px;
}
:host([mode]) .root{
padding: 1px 8px;
}
.root{
position: relative;
padding: 6px 8px;
display: flex;
align-items: center;
justify-content: space-between;
transition: all .3s;
border-radius: 2px;
background-color: #fff;
outline: none;
font-size: 1rem;
z-index: 2;
-webkit-user-select:none ;
-moz-user-select:none;
user-select:none;
width: 100%;
}
.body{
max-height: ${this.listHeight};
position: absolute;
top: 100%;
z-index: 99;
padding-top: 5px;
margin-top: 2px;
background-color: #fff;
width: 100%;
transition: all 0.2s;
transform: scaleY(.6);
visibility: hidden;
opacity: 0;
transform-origin: top center;
display: block;
flex-direction: column;
box-shadow: 0 5px 15px 0px #00000033;
border-radius: 2px;
overflow: auto;
}
.icon{
pointer-events: none;
}
.noSelect{
-webkit-touch-callout:none; /* iOS Safari */
-webkit-user-select:none;
-khtml-user-select:none; /* Konqueror */
-moz-user-select:none; /* Firefox */
-ms-user-select:none; /* Internet Explorer/Edge */
user-select:none; /* Non-prefixed version */
}
:host(:not([border]):not([disabled]):focus),
:host([border='true']:not([disabled]):focus),
:host(:not([border]):not([disabled]):hover),
:host([border='true']:not([disabled]):hover){
border:1px solid #42b983
}
:host(:not([disabled]):focus) .body,
:host(:not([disabled]):focus-within) .body{
transform: scaleY(1);
opacity: 1;
z-index: 99;
visibility: visible;
}
:host(:not([disabled]):focus) input{
color: #bebebe;
}
:host(:not([border])[disabled]) *,
:host([border='true'][disabled]) *{
background-color: #f5f5f5;
color: #b7b7b7;
cursor: not-allowed;
}
:host([border='false'][disabled]) *{
color: #b7b7b7;
cursor: not-allowed;
}
:host([loading]) .loading{
display: flex;
}
:host([loading]) .icon{
display: none;
}
:host(:not([loading])) .loading{
display: none;
}
:host(:not([loading])) .icon{
display: flex;
}
:host(:not([allow-clear])) .clear{
display: none;
}
.clear{
display: none;
color: #bfbfbf;
}
.clear:hover{
color: #8c8c8c;
}
.search{
display: none;
color: #bfbfbf;
}
.multipleRoot{
display: flex;
flex-direction: column;
flex-wrap: wrap;
flex-flow: wrap;
align-items: center;
width: 100%;
}
.tag{
display: inline-flex;
align-items: center;
background-color: #f5f5f5;
padding: 1px 4px;
height: auto;
font-size: .75rem;
font-weight: bold;
color: #242424;
overflow: auto;
position: relative;
margin-right: 4px;
margin-top: 1px;
margin-bottom: 1px;
}
.tag-close{
font-size: .8rem;
padding: 2px;
margin-left: 0px;
color: #999999;
}
.tag-close:hover{
color: #333;
}
</style>
<div class="root noSelect" tabindex="0" hidefocus="true">
<div class="multipleRoot">
<input placeholder="${this.placeholder}" style="" autocomplete="off" ${this.showSearch ? '' : 'readonly'} tabindex="0">
</div><!--多选-->
<lit-loading class="loading" size="12"></lit-loading>
<!--<lit-icon class="icon" name='down' color="#c3c3c3"></lit-icon>-->
<svg class="icon" viewBox="0 0 1024 1024" aria-hidden="true" style="width: 16px;width: 16px;fill: #c3c3c3">
<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>
</svg>
<!-- <lit-icon class="clear" name='close-circle-fill'></lit-icon>-->
<svg class="clear" viewBox="0 0 1024 1024" aria-hidden="true" style="width: 16px;width: 16px;">
<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>
</svg>
<!-- <lit-icon class="search" name='search'></lit-icon>-->
<svg class="search" viewBox="0 0 1024 1024" aria-hidden="true" style="width: 16px;width: 16px;">
<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>
</svg>
</div>
<div class="body">
<slot></slot>
<slot name="footer"></slot>
</div>
`
}
isMultiple() {
return this.hasAttribute('mode') && this.getAttribute('mode') === 'multiple'
}
newTag(value, text) {
let tag = document.createElement('div');
let icon = document.createElement('lit-icon');
icon.classList.add('tag-close')
icon.name = 'close'
let span = document.createElement('span');
tag.classList.add('tag');
span.dataset['value'] = value;
span.textContent = text;
tag.append(span);
tag.append(icon);
icon.onclick = ev => {
tag.parentElement.removeChild(tag);
this.querySelector(`lit-select-option[value=${value}]`).removeAttribute('selected')
if (this.shadowRoot.querySelectorAll('.tag').length == 0) {
this.inputElement.style.width = 'auto';
this.inputElement.placeholder = this.defaultPlaceholder;
}
ev.stopPropagation();
}
tag.value = value;
tag.dataset['value'] = value;
tag.text = text;
tag.dataset['text'] = text;
return tag;
}
connectedCallback() {
this.tabIndex = 0;
this.focused = false;
this.inputElement = this.shadowRoot.querySelector('input');
this.inputElement.style.width = '100%'
this.clearElement = this.shadowRoot.querySelector('.clear');
this.iconElement = this.shadowRoot.querySelector('.icon');
this.searchElement = this.shadowRoot.querySelector('.search');
this.multipleRootElement = this.shadowRoot.querySelector('.multipleRoot');
this.clearElement.onclick = ev => {
if (this.isMultiple()) {
let delNodes = []
this.multipleRootElement.childNodes.forEach(a => {
if (a.tagName === 'DIV') {
delNodes.push(a);
}
})
for (let i = 0; i < delNodes.length; i++) {
delNodes[i].remove();
}
if (this.shadowRoot.querySelectorAll('.tag').length == 0) {
this.inputElement.style.width = 'auto';
this.inputElement.placeholder = this.defaultPlaceholder;
}
}
this.querySelectorAll('lit-select-option').forEach(a => a.removeAttribute('selected'));
this.inputElement.value = ''
this.clearElement.style.display = 'none';
this.iconElement.style.display = 'flex';
this.blur();
ev.stopPropagation();
this.dispatchEvent(new CustomEvent('onClear', { detail: ev }))
}
this.initOptions();
this.onclick = ev => {
if (ev.target.tagName === 'LIT-SELECT') {
if (!this.focused) {
this.inputElement.focus();
this.focused = true;
} else {
this.blur();
this.focused = false;
}
}
}
this.onmouseover = this.onfocus = ev => {
if (this.hasAttribute('allow-clear')) {
if (this.inputElement.value.length > 0 || this.inputElement.placeholder !== this.defaultPlaceholder) {
this.clearElement.style.display = 'flex'
this.iconElement.style.display = 'none';
} else {
this.clearElement.style.display = 'none'
this.iconElement.style.display = 'flex';
}
}
}
this.onmouseout = this.onblur = ev => {
if (this.hasAttribute('allow-clear')) {
this.clearElement.style.display = 'none';
this.iconElement.style.display = 'flex';
}
this.focused = false;
}
this.inputElement.onfocus = ev => {
if (this.hasAttribute('disabled')) return;
if (this.inputElement.value.length > 0) {
this.inputElement.placeholder = this.inputElement.value;
this.inputElement.value = ''
}
if (this.hasAttribute('show-search')) {
this.searchElement.style.display = 'flex';
this.iconElement.style.display = 'none';
}
this.querySelectorAll('lit-select-option').forEach(a => {
a.style.display = 'flex';
})
}
this.inputElement.onblur = ev => {
if (this.hasAttribute('disabled')) return;
if (this.isMultiple()) {
if (this.hasAttribute('show-search')) {
this.searchElement.style.display = 'none';
this.iconElement.style.display = 'flex';
}
} else {
if (this.inputElement.placeholder !== this.defaultPlaceholder) {
this.inputElement.value = this.inputElement.placeholder;
this.inputElement.placeholder = this.defaultPlaceholder;
}
if (this.hasAttribute('show-search')) {
this.searchElement.style.display = 'none';
this.iconElement.style.display = 'flex';
}
}
}
this.inputElement.oninput = ev => {
let els = [...this.querySelectorAll('lit-select-option')];
if (!ev.target.value) {
els.forEach(a => a.style.display = 'flex');
} else {
els.forEach(a => {
let value = a.getAttribute('value');
if (value.toLowerCase().indexOf(ev.target.value.toLowerCase()) !== -1 ||
a.textContent.toLowerCase().indexOf(ev.target.value.toLowerCase()) !== -1) {
a.style.display = 'flex';
} else {
a.style.display = 'none';
}
})
}
}
this.inputElement.onkeydown = ev => {
if (ev.key === 'Backspace') {
if (this.isMultiple()) {
let tag = this.multipleRootElement.lastElementChild.previousElementSibling;
if (tag) {
console.log(tag.value);
this.querySelector(`lit-select-option[value=${tag.value}]`).removeAttribute('selected');
tag.remove()
if (this.shadowRoot.querySelectorAll('.tag').length == 0) {
this.inputElement.style.width = 'auto';
this.inputElement.placeholder = this.defaultPlaceholder;
}
}
} else {
this.clear();
this.dispatchEvent(new CustomEvent('onClear', { detail: ev }))
}
} else if (ev.key === 'Enter') {
let filter = [...this.querySelectorAll('lit-select-option')].filter(a => a.style.display !== 'none');
if (filter.length > 0) {
this.inputElement.value = filter[0].textContent;
this.inputElement.placeholder = filter[0].textContent;
this.blur();
this.dispatchEvent(new CustomEvent('change', {
detail: {
selected: true,
value: filter[0].getAttribute('value'),
text: filter[0].textContent
}
}));
}
}
}
}
initOptions() {
this.querySelectorAll('lit-select-option').forEach(a => {
if (this.isMultiple()) {
a.setAttribute('check', '');
if (a.getAttribute('value') === this.defaultValue) {
let tag = this.newTag(a.getAttribute('value'), a.textContent);
this.multipleRootElement.insertBefore(tag, this.inputElement);
this.inputElement.placeholder = '';
this.inputElement.value = '';
this.inputElement.style.width = '1px';
a.setAttribute('selected', '');
}
} else {
if (a.getAttribute('value') === this.defaultValue) {
this.inputElement.value = a.textContent;
a.setAttribute('selected', '');
}
}
a.addEventListener('onSelected', (e) => {
if (this.isMultiple()) {
if (a.hasAttribute('selected')) {
let tag = this.shadowRoot.querySelector(`div[data-value=${e.detail.value}]`);
tag.parentElement.removeChild(tag);
e.detail.selected = false;
} else {
let tag = this.newTag(e.detail.value, e.detail.text);
this.multipleRootElement.insertBefore(tag, this.inputElement);
this.inputElement.placeholder = '';
this.inputElement.value = '';
this.inputElement.style.width = '1px';
}
if (this.shadowRoot.querySelectorAll('.tag').length == 0) {
this.inputElement.style.width = 'auto';
this.inputElement.placeholder = this.defaultPlaceholder;
}
this.inputElement.focus();
} else {
[...this.querySelectorAll('lit-select-option')].forEach(a => a.removeAttribute('selected'))
this.blur();
this.inputElement.value = e.detail.text;
}
if (a.hasAttribute('selected')) {
a.removeAttribute('selected')
} else {
a.setAttribute('selected', '')
}
this.dispatchEvent(new CustomEvent('change', { detail: e.detail }));
})
})
}
clear() {
this.inputElement.value = '';
this.inputElement.placeholder = this.defaultPlaceholder;
}
reset() {
this.querySelectorAll('lit-select-option').forEach(a => {
[...this.querySelectorAll('lit-select-option')].forEach(a => a.removeAttribute('selected'))
if (a.getAttribute('value') === this.defaultValue) {
this.inputElement.value = a.textContent;
a.setAttribute('selected', '');
}
})
}
disconnectedCallback() {
}
adoptedCallback() {
console.log('Custom square element moved to new page.');
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'value' && this.inputElement) {
if (newValue) {
[...this.querySelectorAll('lit-select-option')].forEach(a => {
if (a.getAttribute('value') === newValue) {
a.setAttribute('selected', '');
this.inputElement.value = a.textContent;
} else {
a.removeAttribute('selected')
}
})
} else {
this.clear();
}
}
}
set dataSource(value) {
value.forEach(a => {
let option = document.createElement('lit-select-option');
option.setAttribute('value', a.key);
option.textContent = a.val;
this.append(option)
})
this.initOptions();
}
}
if (!customElements.get('lit-select')) {
customElements.define('lit-select', LitSelect);
}
class LitSelectGroup extends HTMLElement {
static get observedAttributes() {
return ['label']
}
get label() {
return this.getAttribute('label') || '';
}
set label(value) {
this.setAttribute('label', value);
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
display: flex;
flex-direction: column;
/*padding-left: 10px;*/
}
.lab{
padding: 8px 10px 8px 10px;
font-size: .5rem;
color: #8c8c8c;
}
::slotted(lit-select-option){
padding-left: 20px;
}
</style>
<div class="lab">${this.label}</div>
<slot></slot>
`
}
connectedCallback() {
}
disconnectedCallback() {
}
adoptedCallback() {
console.log('Custom square element moved to new page.');
}
attributeChangedCallback(name, oldValue, newValue) {
}
}
if (!customElements.get('lit-select-group')) {
customElements.define('lit-select-group', LitSelectGroup);
}
class LitSelectOption extends HTMLElement {
static get observedAttributes() {
return ['selected', 'disabled', 'check']
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
display: flex;
padding: 8px 10px;
transition: all .3s;
color: #333;
tab-index: -1;
overflow: scroll;
align-items: center;
justify-content: space-between;
}
:host(:not([disabled])[selected]){
background-color: #e9f7fe;
font-weight: bold;
}
:host(:not([disabled]):not([selected]):hover){
background-color: #f5f5f5;
}
:host([disabled]){
cursor: not-allowed;
color: #bfbfbf;
}
:host([selected][check]) .check{
display: flex;
}
:host(:not([selected])) .check{
display: none;
}
:host(:not([check])) .check{
display: none;
}
</style>
<slot></slot>
<lit-icon class="check" name="check"></lit-icon>
`
}
connectedCallback() {
if (!this.hasAttribute('disabled')) {
this.onclick = ev => {
this.dispatchEvent(new CustomEvent('onSelected', {
detail: {
selected: true,
value: this.getAttribute('value'),
text: this.textContent
}
}))
}
}
}
disconnectedCallback() {
}
adoptedCallback() {
console.log('Custom square element moved to new page.');
}
attributeChangedCallback(name, oldValue, newValue) {
}
}
if (!customElements.get('lit-select-option')) {
customElements.define('lit-select-option', LitSelectOption);
}
class LitTable extends HTMLElement {
static get observedAttributes() {
return ['scroll-y', 'selectable', 'defaultOrderColumn']
}
get selectable() {
return this.hasAttribute('selectable');
}
set selectable(value) {
if (value) {
this.setAttribute('selectable', '');
} else {
this.removeAttribute('selectable');
}
}
get scrollY() {
return this.getAttribute('scroll-y') || 'auto';
}
set scrollY(value) {
this.setAttribute('scroll-y', value);
}
get dataSource() {
return this.ds || [];
}
set dataSource(value) {
this.ds = value;
if (this.hasAttribute('tree')) {
this.renderTreeTable();
} else {
this.renderTable();
}
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
display: grid;
grid-template-columns: repeat(1,1fr);
overflow: auto;
/*width: 500px;*/
width: 100%;
height: 100%;
}
.tr{
display: grid;
transition: all .3s;
}
.tr:nth-of-type(even){
background-color: #fcfcfc;
}
/*.tr:not(:last-of-type):not(:first-of-type){*/
/* border-top: 1px solid #f0f0f0;*/
/*}*/
/*.tr:last-of-type{*/
/* border-top: 1px solid #f0f0f0;*/
/* border-bottom: 1px solid #f0f0f0;*/
/*}*/
.tr{
background-color: #fff;
}
.tr:hover{
background-color: #f3f3f3; /*antd #fafafa 42b983*/
}
.td{
background-color: inherit;
box-sizing: border-box;
padding: 10px;
display: flex;
justify-content: flex-start;
align-items: center;
width: 100%;
height: auto;
/*overflow: auto;*/
border-left: 1px solid #f0f0f0;
}
.td-order{
/*background: green;*/
}
.td-order:before{
}
.td:last-of-type{
border-right: 1px solid #f0f0f0;
}
.table{
color: #262626;
}
:host(:not([noheader])) .thead{
display: grid;
position: sticky;
top: 0;
font-weight: bold;
font-size: .9rem;
color: #fff;
/*width: 100%;*/
background-color: #42b983;
z-index: 1;
}
/*配置有 noheader 表示不限时表头,tbody上添加 border-top*/
:host([noheader]) .thead{
display: none;
position: sticky;
top: 0;
font-weight: bold;
font-size: .9rem;
color: #fff;
/*width: 100%;*/
background-color: #42b983;
z-index: 1;
}
:host([noheader]) .tbody{
border-top: 1px solid #f0f0f0;
}
.tbody{
width: 100%;
height: ${this.scrollY};
display: grid;
grid-template-columns: 1fr;
row-gap: 1px;
column-gap: 1px;
background-color: #f0f0f0;
border-bottom: 1px solid #f0f0f0;
/*overflow: auto;*/
${this.scrollY === 'auto' ? '' : 'overflow-y: auto'};
}
.th{
display: grid;
background-color: #42b983;
/*position: sticky;*/
/*top: 0;*/
}
.tree-icon{
font-size: 1.2rem;
width: 20px;
height: 20px;
padding-right: 5px;
padding-left: 5px;
cursor: pointer;
}
.tree-icon:hover{
color: #42b983;
}
.row-checkbox,row-checkbox-all{
}
.up-svg{
position: absolute;
right: 5px;
top: 8px;
width: 15px;
height: 15px;
}
.down-svg{
position: absolute;
right: 5px;
bottom: 8px;
width: 15px;
height: 15px;
}
</style>
<slot id="slot" style="display: none"></slot>
<div class="table">
<div class="thead"></div>
<div class="tbody"></div>
</div>
`
}
connectedCallback() {
this.st = this.shadowRoot.querySelector('#slot');
this.tableElement = this.shadowRoot.querySelector('.table');
this.theadElement = this.shadowRoot.querySelector('.thead');
this.tbodyElement = this.shadowRoot.querySelector('.tbody');
this.tableColumns = this.querySelectorAll('lit-table-column');
this.colCount = this.tableColumns.length;
this.st.addEventListener('slotchange', (event) => {
this.theadElement.innerHTML = '';
setTimeout(() => {
this.columns = this.st.assignedElements();
let rowElement = document.createElement('div');
rowElement.classList.add('th');
if (this.selectable) {
let box = document.createElement('div');
box.style.display = 'flex';
box.style.justifyContent = 'center';
box.style.alignItems = 'center';
box.style.gridArea = "_checkbox_";
box.classList.add('td');
box.style.backgroundColor = "#ffffff66";
let checkbox = document.createElement('lit-checkbox');
checkbox.classList.add('row-checkbox-all');
checkbox.onchange = e => {
this.shadowRoot.querySelectorAll('.row-checkbox').forEach(a => a.checked = e.detail.checked);
if (e.detail.checked) {
this.shadowRoot.querySelectorAll('.tr').forEach(a => a.setAttribute('checked', ''));
} else {
this.shadowRoot.querySelectorAll('.tr').forEach(a => a.removeAttribute('checked'));
}
}
box.appendChild(checkbox);
rowElement.appendChild(box);
}
let area = [], gridTemplateColumns = [];
let resolvingArea = (columns, x, y) => {
columns.forEach((a, i) => {
if (!area[y]) area[y] = []
let key = a.getAttribute('key') || a.getAttribute('title')
if (a.tagName === 'LIT-TABLE-GROUP') {
let len = a.querySelectorAll('lit-table-column').length;
let children = [...a.children].filter(a => a.tagName !== 'TEMPLATE');
if (children.length > 0) {
resolvingArea(children, x, y + 1);
}
for (let j = 0; j < len; j++) {
area[y][x] = { x, y, t: key };
x++;
}
let h = document.createElement('div');
h.classList.add('td');
h.style.justifyContent = a.getAttribute('align')
h.style.borderBottom = '1px solid #f0f0f0'
h.style.gridArea = key;
h.innerText = a.title;
if (a.hasAttribute('fixed')) {
this.fixed(h, a.getAttribute('fixed'), "#42b983")
}
rowElement.append(h);
} else if (a.tagName === 'LIT-TABLE-COLUMN') {
area[y][x] = { x, y, t: key };
x++;
let h = document.createElement('div');
h.classList.add('td');
if (a.hasAttribute('order')) {
h.sortType = 0;
h.classList.add('td-order');
h.style.position = "relative"
let NS = "http://www.w3.org/2000/svg";
let upSvg = document.createElementNS(NS, "svg");
let upPath = document.createElementNS(NS, "path");
upSvg.setAttribute('fill', '#efefef');
upSvg.setAttribute('viewBox', '0 0 1024 1024');
upSvg.setAttribute('stroke', '#000000');
upSvg.classList.add('up-svg');
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");
upSvg.appendChild(upPath);
let downSvg = document.createElementNS(NS, "svg");
let downPath = document.createElementNS(NS, "path");
downSvg.setAttribute('fill', '#efefef');
downSvg.setAttribute('viewBox', '0 0 1024 1024');
downSvg.setAttribute('stroke', '#efefef');
downSvg.classList.add('down-svg');
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");
downSvg.appendChild(downPath)
if (i == 0) {
h.sortType = 2;
upSvg.setAttribute('fill', '#efefef');
downSvg.setAttribute('fill', '#333');
}
h.appendChild(upSvg);
h.appendChild(downSvg);
h.onclick = ev => {
this.shadowRoot.querySelectorAll('.td-order svg').forEach(it => {
it.setAttribute('fill', '#efefef');
it.setAttribute('fill', '#efefef');
it.sortType = 0;
})
if (h.sortType == undefined || h.sortType == null) {
h.sortType = 0;
} else if (h.sortType === 2) {
h.sortType = 0;
} else {
h.sortType += 1;
}
switch (h.sortType) {
case 1:
upSvg.setAttribute('fill', '#333');
downSvg.setAttribute('fill', '#efefef');
break;
case 2:
upSvg.setAttribute('fill', '#efefef');
downSvg.setAttribute('fill', '#333');
break;
default:
upSvg.setAttribute('fill', "#efefef");
downSvg.setAttribute('fill', "#efefef");
break;
}
this.dispatchEvent(new CustomEvent("ColumnClick", {
detail: {
sort: h.sortType, key: key
}, composed: true
}))
}
}
h.style.justifyContent = a.getAttribute('align')
gridTemplateColumns.push(a.getAttribute('width') || '1fr');
h.style.gridArea = key;
let titleLabel = document.createElement("label");
titleLabel.textContent = a.title;
h.appendChild(titleLabel);
if (a.hasAttribute('fixed')) {
this.fixed(h, a.getAttribute('fixed'), "#42b983")
}
rowElement.append(h);
}
})
}
resolvingArea(this.columns, 0, 0);
area.forEach((rows, j, array) => {
for (let i = 0; i < this.colCount; i++) {
if (!rows[i]) rows[i] = array[j - 1][i];
}
})
this.gridTemplateColumns = gridTemplateColumns.join(' ');
if (this.selectable) {
let s = area.map(a => '"_checkbox_ ' + (a.map(aa => aa.t).join(' ')) + '"').join(' ');
rowElement.style.gridTemplateColumns = "60px " + gridTemplateColumns.join(' ');
rowElement.style.gridTemplateRows = `repeat(${area.length},1fr)`
rowElement.style.gridTemplateAreas = s
} else {
let s = area.map(a => '"' + (a.map(aa => aa.t).join(' ')) + '"').join(' ');
rowElement.style.gridTemplateColumns = gridTemplateColumns.join(' ');
rowElement.style.gridTemplateRows = `repeat(${area.length},1fr)`
rowElement.style.gridTemplateAreas = s
}
this.theadElement.append(rowElement);
if (this.hasAttribute('tree')) {
this.renderTreeTable();
} else {
this.renderTable();
}
});
});
}
disconnectedCallback() {
}
adoptedCallback() {
console.log('Custom square element moved to new page.');
}
attributeChangedCallback(name, oldValue, newValue) {
}
fixed(td, placement, bgColor, zIndex) {
td.style.position = 'sticky';
if (placement === "left") {
td.style.left = '0px';
td.style.boxShadow = '3px 0px 5px #33333333'
} else if (placement === "right") {
td.style.right = '0px';
td.style.boxShadow = '-3px 0px 5px #33333333'
}
}
renderTable() {
let that = this;
if (!this.columns) return;
if (!this.ds) return;
this.tbodyElement.innerHTML = '';
this.ds.forEach(rowData => {
let rowElement = document.createElement('div');
rowElement.classList.add('tr');
rowElement.data = rowData;
let gridTemplateColumns = []
if (this.selectable) {
let box = document.createElement('div');
box.style.display = 'flex';
box.style.justifyContent = 'center';
box.style.alignItems = 'center';
box.classList.add('td');
let checkbox = document.createElement('lit-checkbox');
checkbox.classList.add('row-checkbox');
checkbox.onchange = (e) => {
if (e.detail.checked) {
rowElement.setAttribute('checked', "");
} else {
rowElement.removeAttribute('checked');
}
}
box.appendChild(checkbox);
rowElement.appendChild(box);
}
this.tableColumns.forEach(cl => {
let dataIndex = cl.getAttribute('data-index');
gridTemplateColumns.push(cl.getAttribute('width') || '1fr')
if (cl.template) {
let cloneNode = cl.template.render(rowData).content.cloneNode(true);
let d = document.createElement('div');
d.classList.add('td');
d.style.justifyContent = cl.getAttribute('align')
if (cl.hasAttribute('fixed')) {
this.fixed(d, cl.getAttribute('fixed'), "#ffffff")
}
d.append(cloneNode);
rowElement.append(d);
} else {
let td = document.createElement('div');
td.classList.add('td');
td.style.justifyContent = cl.getAttribute('align')
if (cl.hasAttribute('fixed')) {
this.fixed(td, cl.getAttribute('fixed'), "#ffffff")
}
td.innerHTML = `<code style="padding:0;margin:0">${rowData[dataIndex].toString().replace('\n', "")}</code>`;
rowElement.append(td);
}
})
if (this.selectable) {
rowElement.style.gridTemplateColumns = '60px ' + gridTemplateColumns.join(' ');
} else {
rowElement.style.gridTemplateColumns = gridTemplateColumns.join(' ');
}
rowElement.onclick = e => {
this.dispatchEvent(new CustomEvent('onRowClick', { detail: rowData, composed: true }));
}
this.tbodyElement.append(rowElement);
})
}
renderTreeTable() {
if (!this.columns) return;
if (!this.ds) return;
this.tbodyElement.innerHTML = '';
let ids = JSON.parse(this.getAttribute('tree') || `["id","pid"]`);
let toTreeData = (data, id, pid) => {
let cloneData = JSON.parse(JSON.stringify(data));
return cloneData.filter(father => {
let branchArr = cloneData.filter(child => father[id] == child[pid]);
branchArr.length > 0 ? father['children'] = branchArr : '';
return !father[pid];
});
}
let treeData = toTreeData(this.ds, ids[0], ids[1]);
let offset = 30;
let offsetVal = offset;
const drawRow = (arr, parentNode) => {
arr.forEach(rowData => {
let rowElement = document.createElement('div');
rowElement.classList.add('tr');
rowElement.data = rowData;
let gridTemplateColumns = [];
if (this.selectable) {
let box = document.createElement('div');
box.style.display = 'flex';
box.style.justifyContent = 'center';
box.style.alignItems = 'center';
box.classList.add('td');
let checkbox = document.createElement('lit-checkbox');
checkbox.classList.add('row-checkbox');
checkbox.onchange = (e) => {
if (e.detail.checked) {
rowElement.setAttribute('checked', "");
} else {
rowElement.removeAttribute('checked');
}
const changeChildNode = (rowElement, checked) => {
let id = rowElement.getAttribute('id');
let pid = rowElement.getAttribute('pid');
this.shadowRoot.querySelectorAll(`div[pid=${id}]`).forEach(a => {
a.querySelector('.row-checkbox').checked = checked;
if (checked) {
a.setAttribute('checked', '');
} else {
a.removeAttribute('checked');
}
changeChildNode(a, checked);
});
};
changeChildNode(rowElement, e.detail.checked);
}
box.appendChild(checkbox);
rowElement.appendChild(box);
}
this.tableColumns.forEach((cl, index) => {
let dataIndex = cl.getAttribute('data-index');
gridTemplateColumns.push(cl.getAttribute('width') || '1fr')
let td;
if (cl.template) {
let cloneNode = cl.template.render(rowData).content.cloneNode(true);
td = document.createElement('div');
td.classList.add('td');
td.style.justifyContent = cl.getAttribute('align')
if (cl.hasAttribute('fixed')) {
this.fixed(td, cl.getAttribute('fixed'), "#ffffff")
}
td.append(cloneNode);
} else {
td = document.createElement('div');
td.classList.add('td');
td.style.justifyContent = cl.getAttribute('align')
if (cl.hasAttribute('fixed')) {
this.fixed(td, cl.getAttribute('fixed'), "#ffffff")
}
td.innerHTML = rowData[dataIndex];
}
if (index === 0) {
if (rowData.children && rowData.children.length > 0) {
let btn = document.createElement('lit-icon');
btn.classList.add('tree-icon');
btn.name = 'minus-square';
td.insertBefore(btn, td.firstChild);
td.style.paddingLeft = (offsetVal - 30) + 'px';
btn.onclick = (e) => {
const foldNode = (rowElement) => {
let id = rowElement.getAttribute('id');
let pid = rowElement.getAttribute('pid');
this.shadowRoot.querySelectorAll(`div[pid=${id}]`).forEach(a => {
let id = a.getAttribute('id');
let pid = a.getAttribute('pid');
a.style.display = 'none';
foldNode(a);
});
if (rowElement.querySelector('.tree-icon')) {
rowElement.querySelector('.tree-icon').name = 'plus-square';
}
rowElement.removeAttribute('expand');
};
const expendNode = (rowElement) => {
let id = rowElement.getAttribute('id');
let pid = rowElement.getAttribute('pid');
this.shadowRoot.querySelectorAll(`div[pid=${id}]`).forEach(a => {
let id = a.getAttribute('id');
let pid = a.getAttribute('pid');
a.style.display = '';
});
if (rowElement.querySelector('.tree-icon')) {
rowElement.querySelector('.tree-icon').name = 'minus-square';
}
rowElement.setAttribute('expand', '');
}
if (rowElement.hasAttribute('expand')) {
foldNode(rowElement);
} else {
expendNode(rowElement);
}
};
} else {
td.style.paddingLeft = offsetVal + 'px';
}
}
rowElement.append(td);
})
if (this.selectable) {
rowElement.style.gridTemplateColumns = '60px ' + gridTemplateColumns.join(' ');
} else {
rowElement.style.gridTemplateColumns = gridTemplateColumns.join(' ');
}
rowElement.onclick = e => {
}
parentNode.append(rowElement);
rowElement.setAttribute('id', rowData[ids[0]]);
rowElement.setAttribute('pid', rowData[ids[1]]);
rowElement.setAttribute('expand', '');
if (rowData.children && rowData.children.length > 0) {
offsetVal = offsetVal + offset;
drawRow(rowData.children, parentNode);
offsetVal = offsetVal - offset;
}
});
};
drawRow(treeData, this.tbodyElement);
}
getCheckRows() {
return [...this.shadowRoot.querySelectorAll('div[class=tr][checked]')].map(a => a.data).map(a => {
delete a['children'];
return a;
});
}
deleteRowsCondition(fn) {
this.shadowRoot.querySelectorAll("div[class=tr]").forEach(tr => {
if (fn(tr.data)) {
tr.remove();
}
})
}
}
if (!customElements.get('lit-table')) {
customElements.define('lit-table', LitTable);
}
class LitTableColumn extends HTMLElement {
static get observedAttributes() {
return ['name', 'order']
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{ }
</style>
<slot id="slot"></slot>
`
}
connectedCallback() {
this.template = null;
this.st = this.shadowRoot.querySelector('#slot')
this.st.addEventListener('slotchange', () => {
const elements = this.st.assignedElements({ flatten: false });
if (elements.length > 0) {
this.template = elements[0];
}
})
}
disconnectedCallback() {
}
adoptedCallback() {
console.log('Custom square element moved to new page.');
}
attributeChangedCallback(name, oldValue, newValue) {
}
}
if (!customElements.get('lit-table-column')) {
customElements.define('lit-table-column', LitTableColumn);
}
class LitTableGroup extends HTMLElement {
static get observedAttributes() {
return ['title']
}
get title() {
return this.getAttribute('title');
}
set title(value) {
this.setAttribute('title', value);
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{ }
</style>
<slot id="sl"></slot>
`
}
connectedCallback() {
}
disconnectedCallback() {
}
adoptedCallback() {
console.log('Custom square element moved to new page.');
}
attributeChangedCallback(name, oldValue, newValue) {
}
}
if (!customElements.get('lit-table-group')) {
customElements.define('lit-table-group', LitTableGroup);
}
class LitTabpane extends HTMLElement {
static get observedAttributes() { return ['tab', 'key', 'disabled', 'icon', 'closeable', 'hide']; }
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host(){
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
overflow: auto;
width: 100%;
}
</style>
<slot></slot>
`
}
get tab() {
return this.getAttribute('tab');
}
set tab(value) {
this.setAttribute("tab", value);
}
get icon() {
return this.getAttribute("icon");
}
get disabled() {
return this.getAttribute('disabled') !== null;
}
set disabled(value) {
if (value === null || !value) {
this.removeAttribute("disabled");
} else {
this.setAttribute("disabled", value);
}
}
get closeable() {
return this.getAttribute('closeable') !== null;
}
set closeable(value) {
if (value === null || !value) {
this.removeAttribute("closeable");
} else {
this.setAttribute("closeable", value);
}
}
get key() {
return this.getAttribute("key");
}
set key(value) {
this.setAttribute("key", value);
}
get hide() {
return this.getAttribute('hide') !== null;
}
set hide(value) {
if (value === null || !value) {
this.removeAttribute("hide");
} else {
this.setAttribute("hide", value);
}
}
connectedCallback() { }
disconnectedCallback() { }
adoptedCallback() { }
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue && newValue !== undefined) {
if (name === 'tab' && this.parentNode) {
this.parentNode.updateLabel && this.parentNode.updateLabel(this.key, newValue);
}
if (name === 'disabled' && this.parentNode) {
this.parentNode.updateDisabled && this.parentNode.updateDisabled(this.key, newValue);
}
if (name === 'closeable' && this.parentNode) {
this.parentNode.updateCloseable && this.parentNode.updateCloseable(this.key, newValue);
}
if (name === 'hide' && this.parentNode) {
this.parentNode.updateCloseable && this.parentNode.updateHide(this.key, newValue);
}
}
}
}
if (!customElements.get('lit-tabpane')) {
customElements.define('lit-tabpane', LitTabpane);
}
class LitTabs extends HTMLElement {
static get observedAttributes() {
return ['activekey', 'mode', 'position']
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
display: block;
text-align: unset;
color: #252525;
background-color: #fff;
box-shadow: #00000033 0 0 10px ;
/*padding: 10px;*/
/*margin-right: 10px;*/
}
::slotted(lit-tabpane){
box-sizing:border-box;
width:100%;
height:100%;
/*padding:10px;*/
flex-shrink:0;
overflow:auto;
}
.nav-item{
display: inline-flex;
justify-content: center;
align-items: center;
padding: 6px 0px 6px 12px;
font-size: .9rem;
font-weight: normal;
cursor: pointer;
transition: all 0.3s;
flex-shrink: 0;
}
.nav-item lit-icon{
margin-right: 2px;
font-size: inherit;
}
.nav-item:hover{
color: #42b983;
}
.nav-item[data-disabled]{
pointer-events: all;
cursor: not-allowed;
color: #bfbfbf;
}
.nav-item[data-selected]{
color: #42b983;;
}
.tab-content{
display: block;
background-color: #fff;
flex:1;
}
/*
* top top-left top-center top-right
*/
:host(:not([position])) .nav-root,
:host([position^='top']) .nav-root{
display: flex;
position: relative;
justify-content: center;
align-items: center;
}
:host(:not([mode]):not([position])) .tab-line,/*移动的线条*/
:host([mode='flat'][position^='top']) .tab-line{
position:absolute;
bottom: 2px;
background-color: #42b983;
height: 2px;
transform: translateY(100%);
transition: all 0.3s;
}
:host(:not([position])) .tab-nav-container,
:host([position^='top']) .tab-nav-container{
display: flex;
position: relative;
flex-direction: column;
overflow-y: hidden;
overflow-x: auto;
overflow: -moz-scrollbars-none;
-ms-overflow-style: none;
transition: all 0.3s;
flex: 1;
/*background: linear-gradient(90deg,white 30%, transparent),radial-gradient(at 0 50%, rgba(0,0,0,.2),transparent 70%);*/
/*background-repeat: no-repeat;*/
/*background-size: 50px 100%, 15px 100%;*/
/*background-attachment: local,scroll,local,scroll;*/
/*border-bottom: #f0f0f0 1px solid;*/
}
:host([position='top']) .tab-nav,
:host([position='top-left']) .tab-nav{
display: flex;
position: relative;
justify-content: flex-start;
}
:host([position='top-center']) .tab-nav{
display: flex;
justify-content: center;
}
:host([position='top-right']) .tab-nav{
display: flex;
justify-content: flex-end;
}
:host([position^='top'][mode='card']) .nav-item{
border-top: 1px solid #f0f0f0;
border-left: 1px solid #f0f0f0;
border-right: 1px solid #f0f0f0;
border-bottom: 1px solid #f0f0f0;
bottom: 0px;
margin-right: 2px;
position: relative;
}
:host([position^='top']) .tab-nav-bg-line{
position: absolute;bottom: 0;height: 1px;background-color: #f0f0f0;width: 100%
}
:host([position^='top'][mode='card']) .nav-item:not([data-selected]){
background-color: #f6f6f6;
border-bottom: 1px solid #f0f0f0;
}
:host([position^='top'][mode='card']) .nav-item[data-selected]{
background-color: #ffffff;
border-bottom: 1px solid #fff;
bottom: 0px;
}
/*
bottom bottom-left bottom-center bottom-right
*/
:host([position^='bottom']) .tab{
display: flex;
flex-direction: column-reverse;
}
:host([mode='flat'][position^='bottom']) .tab-line{
position:absolute;
top: -3px;
background-color: #42b983;
height: 2px;
transform: translateY(-100%);
transition: all 0.3s;
}
:host([position^='bottom']) .tab-nav-container{
display: flex;
position: relative;
flex-direction: column;
overflow-x: auto;
overflow-y: visible;
overflow: -moz-scrollbars-none;
-ms-overflow-style: none;
transition: all 0.3s;
flex: 1;
/*background: linear-gradient(90deg,white 30%, transparent),radial-gradient(at 0 50%, rgba(0,0,0,.2),transparent 70%);*/
/*background-repeat: no-repeat;*/
/*background-size: 50px 100%, 15px 100%;*/
/*background-attachment: local,scroll,local,scroll;*/
border-top: #f0f0f0 1px solid;
}
:host([position^='bottom']) .nav-root{
display: flex;
justify-content: center;
align-items: center;
}
:host([position='bottom']) .tab-nav,
:host([position='bottom-left']) .tab-nav{
display: flex;
position: relative;
justify-content: flex-start;
}
:host([position='bottom-center']) .tab-nav{
display: flex;
justify-content: center;
}
:host([position='bottom-right']) .tab-nav{
display: flex;
justify-content: flex-end;
}
:host([position^='bottom'][mode='card']) .nav-item{
border-top: 1px solid #ffffff;
border-left: 1px solid #f0f0f0;
border-right: 1px solid #f0f0f0;
border-bottom: 1px solid #f0f0f0;
top: -1px;
margin-right: 2px;
position: relative;
}
:host([position^='bottom']) .tab-nav-bg-line{
position: absolute;top: 0;height: 1px;background-color: #f0f0f0;width: 100%
}
:host([position^='bottom'][mode='card']) .nav-item:not([data-selected]){
background-color: #f5f5f5;
border-top: 1px solid #f0f0f0;
}
:host([position^='bottom'][mode='card']) .nav-item[data-selected]{
background-color: #ffffff;
border-top: 1px solid #fff;
top: -1px;
}
/*
left left-top left-center left-bottom
*/
:host([position^='left']) .tab{
display: flex;
flex-direction: row;
}
:host([mode='flat'][position^='left']) .tab-line{
position:absolute;
right: 1px;
background-color: #42b983;
width: 3px;
transform: translateX(100%);
transition: all 0.3s;
}
:host([position^='left']) .tab-nav-container{
display: flex;
position: relative;
flex-direction: row;
overflow-x: auto;
overflow-y: visible;
overflow: -moz-scrollbars-none;
-ms-overflow-style: none;
transition: all 0.3s;
flex: 1;
/*background: linear-gradient(90deg,white 30%, transparent),radial-gradient(at 0 50%, rgba(0,0,0,.2),transparent 70%);*/
/*background-repeat: no-repeat;*/
/*background-size: 50px 100%, 15px 100%;*/
/*background-attachment: local,scroll,local,scroll;*/
border-right: #f0f0f0 1px solid;
}
:host([position^='left']) .nav-root{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
:host([position='left']) .tab-nav,
:host([position='left-top']) .tab-nav{
display: flex;
position: relative;
flex-direction: column;
justify-content: flex-start;
}
:host([position='left-center']) .tab-nav{
display: flex;
position: relative;
flex-direction: column;
justify-content: center;
}
:host([position='left-bottom']) .tab-nav{
display: flex;
position: relative;
flex-direction: column;
justify-content: flex-end;
}
:host([position^='left'][mode='card']) .nav-item{
border-top: 1px solid #f0f0f0;
border-left: 1px solid #f0f0f0;
border-right: 1px solid #ffffff;
border-bottom: 1px solid #f0f0f0;
right: -1px;
margin-bottom: 2px;
position: relative;
}
:host([position^='left']) .tab-nav-bg-line{
position: absolute;right: 0;width: 1px;background-color: #f0f0f0;width: 100%
}
:host([position^='left'][mode='card']) .nav-item:not([data-selected]){
background-color: #f5f5f5;
border-right: 1px solid #f0f0f0;
}
:host([position^='left'][mode='card']) .nav-item[data-selected]{
background-color: #ffffff;
border-bottom: 1px solid #fff;
right: -1px;
}
/*
right right-top right-center right-bottom
*/
:host([position^='right']) .tab{
display: flex;
flex-direction: row-reverse;
}
:host([mode='flat'][position^='right']) .tab-line{
position:absolute;
left: 1px;
background-color: #42b983;
width: 3px;
transform: translateX(-100%);
transition: all 0.3s;
}
:host([position^='right']) .tab-nav-container{
display: flex;
position: relative;
flex-direction: row-reverse;
overflow-x: auto;
overflow-y: visible;
overflow: -moz-scrollbars-none;
-ms-overflow-style: none;
transition: all 0.3s;
flex: 1;
/*background: linear-gradient(90deg,white 30%, transparent),radial-gradient(at 0 50%, rgba(0,0,0,.2),transparent 70%);*/
/*background-repeat: no-repeat;*/
/*background-size: 50px 100%, 15px 100%;*/
/*background-attachment: local,scroll,local,scroll;*/
border-left: #f0f0f0 1px solid;
}
:host([position^='right']) .nav-root{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
:host([position='right']) .tab-nav,
:host([position='right-top']) .tab-nav{
display: flex;
position: relative;
flex-direction: column;
justify-content: flex-start;
}
:host([position='right-center']) .tab-nav{
display: flex;
position: relative;
flex-direction: column;
justify-content: center;
}
:host([position='right-bottom']) .tab-nav{
display: flex;
position: relative;
flex-direction: column;
justify-content: flex-end;
}
:host([position^='right'][mode='card']) .nav-item{
border-top: 1px solid #f0f0f0;
border-left: 1px solid #ffffff;
border-right: 1px solid #f0f0f0;
border-bottom: 1px solid #f0f0f0;
left: -1px;
margin-top: 2px;
position: relative;
}
:host([position^='right']) .tab-nav-bg-line{
position: absolute;left: 0;width: 1px;background-color: #f0f0f0;width: 100%
}
:host([position^='right'][mode='card']) .nav-item:not([data-selected]){
background-color: #f5f5f5;
border-left: 1px solid #f0f0f0;
}
:host([position^='right'][mode='card']) .nav-item[data-selected]{
background-color: #ffffff;
left: -1px;
}
.tab-nav-container::-webkit-scrollbar {
display: none;
}
/*关闭的图标*/
.close-icon:hover{
color: #000;
}
.nav-item[data-closeable] .close-icon{
display: block;
padding: 5px 5px 5px 5px;
color: #999;
}
.nav-item[data-closeable] .no-close-icon{
display: none;
}
.nav-item:not([data-closeable]) .no-close-icon{
display: block;
}
.nav-item:not([data-closeable]) .close-icon{
display: none;
}
.nav-item:not([data-hide]){
display: block;
}
.nav-item[data-hide]{
display: none;
}
</style>
<style id="filter"></style>
<div class="tab">
<div class="nav-root">
<slot name="left" style="flex:1"></slot>
<div class="tab-nav-container" >
<div class="tab-nav-bg-line"></div>
<div class="tab-nav" id="nav"></div>
<div class="tab-line" id="tab-line"></div>
</div>
<slot name="right" style="flex:1"></slot>
</div>
<div class="tab-content">
<slot id="slot">NEED CONTENT</slot>
</div>
</div>
`
}
get position() {
return this.getAttribute('position') || 'top';
}
set position(value) {
this.setAttribute('position', value);
}
get mode() {
return this.getAttribute('mode') || 'flat';
}
set mode(value) {
this.setAttribute('mode', value);
}
get activekey() {
return this.getAttribute("activekey");
}
set activekey(value) {
this.setAttribute('activekey', value);
}
updateLabel(key, value) {
if (this.nav) {
let item = this.nav.querySelector(`.nav-item[data-key='${key}']`);
if (item) {
item.querySelector("span").innerHTML = value;
this.initTabPos()
}
}
}
updateDisabled(key, value) {
if (this.nav) {
let item = this.nav.querySelector(`.nav-item[data-key='${key}']`);
if (item) {
if (value) {
item.setAttribute('data-disabled', '')
} else {
item.removeAttribute('data-disabled');
}
this.initTabPos()
}
}
}
updateCloseable(key, value) {
if (this.nav) {
let item = this.nav.querySelector(`.nav-item[data-key='${key}']`);
if (item) {
if (value) {
item.setAttribute('data-closeable', '')
} else {
item.removeAttribute('data-closeable');
}
this.initTabPos()
}
}
}
updateHide(key, value) {
if (this.nav) {
let item = this.nav.querySelector(`.nav-item[data-key='${key}']`);
if (item) {
if (value) {
item.setAttribute('data-hide', '')
} else {
item.removeAttribute('data-hide');
}
this.initTabPos()
}
}
}
initTabPos() {
const items = this.nav.querySelectorAll(".nav-item");
Array.from(items).forEach((a, index) => {
this.tabPos[a.dataset.key] = {
index: index,
width: a.offsetWidth,
height: a.offsetHeight,
left: a.offsetLeft,
top: a.offsetTop,
label: a.textContent
}
})
if (this.activekey) {
if (this.position.startsWith('left')) {
this.line.style = `height:${this.tabPos[this.activekey].height}px;transform:translate(100%,${this.tabPos[this.activekey].top}px)`;
} else if (this.position.startsWith('top')) {
if (this.tabPos[this.activekey]) {
this.line.style = `width:${this.tabPos[this.activekey].width}px;transform:translate(${this.tabPos[this.activekey].left}px,100%)`;
}
} else if (this.position.startsWith('right')) {
this.line.style = `height:${this.tabPos[this.activekey].height}px;transform:translate(-100%,${this.tabPos[this.activekey].top}px)`;
} else if (this.position.startsWith('bottom')) {
this.line.style = `width:${this.tabPos[this.activekey].width}px;transform:translate(${this.tabPos[this.activekey].left}px,100%)`;
}
}
}
connectedCallback() {
let that = this;
this.tabPos = {}
this.nav = this.shadowRoot.querySelector('#nav')
this.line = this.shadowRoot.querySelector('#tab-line')
this.slots = this.shadowRoot.querySelector('#slot');
this.slots.addEventListener('slotchange', () => {
const elements = this.slots.assignedElements();
let panes = this.querySelectorAll('lit-tabpane');
if (this.activekey) {
panes.forEach(a => {
if (a.key === this.activekey) {
a.style.display = 'block'
} else {
a.style.display = 'none';
}
})
} else {
panes.forEach((a, index) => {
if (index === 0) {
a.style.display = 'block'
this.activekey = a.key
} else {
a.style.display = 'none';
}
})
}
let navHtml = "";
elements.forEach(a => {
if (a.disabled) {
navHtml += `<div class="nav-item" data-key="${a.key}" data-disabled ${a.closeable ? 'data-closeable' : ''} ${a.hide ? 'data-hide' : ''}>
${a.icon ? `<lit-icon name='${a.icon}'></lit-icon>` : ``}
<span>${a.tab}</span>
<lit-icon class="close-icon" name='close' size="12"></lit-icon><div class="no-close-icon" style="margin-right: 12px"></div>
</div>`;
} else {
if (a.key === this.activekey) {
navHtml += `<div class="nav-item" data-key="${a.key}" data-selected ${a.closeable ? 'data-closeable' : ''} ${a.hide ? 'data-hide' : ''}>
${a.icon ? `<lit-icon name='${a.icon}'></lit-icon>` : ``}
<span>${a.tab}</span>
<lit-icon class="close-icon" name='close' size="12"></lit-icon><div class="no-close-icon" style="margin-right: 12px"></div>
</div>`;
} else {
navHtml += `<div class="nav-item" data-key="${a.key}" ${a.closeable ? 'data-closeable' : ''} ${a.hide ? 'data-hide' : ''}>
${a.icon ? `<lit-icon name='${a.icon}'></lit-icon>` : ``}
<span>${a.tab}</span>
<lit-icon class="close-icon" name='close' size="12"></lit-icon><div class="no-close-icon" style="margin-right: 12px"></div>
</div>`;
}
}
})
this.nav.innerHTML = navHtml;
this.initTabPos()
this.nav.querySelectorAll('.close-icon').forEach(a => {
a.onclick = (e) => {
e.stopPropagation();
const closeKey = e.target.parentElement.dataset.key;
console.log(closeKey);
console.log(e.target.parentElement.parentElement);
this.nav.removeChild(e.target.parentElement)
let elements = this.slots.assignedElements();
let closeElement = elements.filter(a => a.key === closeKey)[0];
closeElement.parentElement.removeChild(closeElement)
if (closeElement.style.display !== 'none') {
elements = this.slots.assignedElements();
let elArr = elements.filter(a => !a.hasAttribute('disabled'));
if (elArr.length > 0) {
elArr[0].style.display = 'block';
this.activekey = elArr[0].key
}
}
}
});
})
this.nav.onclick = (e) => {
if (e.target.closest('div').hasAttribute('data-disabled')) return;
let key = e.target.closest('div').dataset.key;
this.activeByKey(key)
let label = e.target.closest('div').querySelector('span').textContent;
this.dispatchEvent(new CustomEvent('onTabClick', { detail: { key: key, tab: label } }))
};
}
set onTabClick(fn) {
this.addEventListener('onTabClick', fn);
}
activeByKey(key) {
if (key === null || key === undefined) return;
this.nav.querySelectorAll('.nav-item').forEach(a => {
if (a.getAttribute('data-key') === key) {
a.setAttribute('data-selected', 'true');
} else {
a.removeAttribute('data-selected');
}
})
let tbp = this.querySelector(`lit-tabpane[key='${key}']`);
let panes = this.querySelectorAll('lit-tabpane');
panes.forEach(a => {
if (a.key === key) {
a.style.display = 'block';
this.activekey = a.key;
this.initTabPos()
} else {
a.style.display = 'none';
}
})
}
activePane(key) {
if (key === null || key === undefined) return false;
let tbp = this.querySelector(`lit-tabpane[key='${key}']`);
if (tbp) {
this.activeByKey(key)
return true;
} else {
return false;
}
}
disconnectedCallback() {
}
adoptedCallback() {
console.log('Custom square element moved to new page.');
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'activekey' && this.nav && oldValue !== newValue) {
this.activeByKey(newValue)
}
}
}
if (!customElements.get('lit-tabs')) {
customElements.define('lit-tabs', LitTabs);
}
class AppDiffFlame extends HTMLElement {
draw;
drawC;
rowHeight = 17;
static get observedAttributes() {
return []
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
font-size:inherit;
display:inline-flex;
align-items: center;
justify-content:center;
height: 100%;
width: 100%;
margin-bottom: 40px;
}
canvas { border: 1px solid #e9e9e9; }
#title{
font-weight: bold;
height: auto;
}
#title span{
color: gray;
}
#funcNameSpan{
display: inline-block;
border: 1px solid #e9e9e9;
box-sizing: border-box;
border-radius: 2px;
padding: 2px 8px;
background: #fff;
color: gray;
flex: 3;
margin-left: 10px;
}
#percentSpan{
display: inline-block;
border: 1px solid #e9e9e9;
margin-left: 10px;
box-sizing: border-box;
border-radius: 2px;
padding: 2px 8px;
background: #fff;
min-width: 160px;
max-width: 160px;
margin-left: 10px;
width: 100px;
height: 30px;
color: gray;
}
#diffSpan{
display: inline-block;
border: 1px solid #e9e9e9;
margin-left: 10px;
box-sizing: border-box;
border-radius: 2px;
padding: 2px 8px;
background: #fff;
min-width: 160px;
max-width: 160px;
margin-left: 10px;
width: 100px;
height: 30px;
color: gray;
}
#history{
display: none;
border: 1px solid #42b983;
box-sizing: border-box;
border-radius: 2px;
padding: 2px 8px;
background: #fff;
width: 100px;
margin-left: 10px;
height: 30px;
cursor: pointer;
user-select: none;
}
#history:hover{
background: #42b983;
color: #fff;
}
#searchInput{
height: 30px;
margin-left: 10px;
margin-right: 10px;
flex: 1;
}
</style>
<div style="position: relative;width: 100%">
<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>
<canvas id="panel" title=""></canvas>
<div id="controller" style="position: absolute;top: 32px;display: flex;width: 100%">
<span id="history">Zoom Out</span>
<span id="funcNameSpan"></span>
<span id="diffSpan"></span>
<span id="percentSpan"></span>
<lit-input id="searchInput" placeholder="search" allow-clear>Search</lit-input>
</div>
</div>
<slot></slot>
`
}
connectedCallback() {
this.history = [];
this.historyRefer = [];
this.panel = this.shadowRoot.getElementById('panel');
this.controller = this.shadowRoot.getElementById('controller');
this.funcNameSpan = this.shadowRoot.getElementById('funcNameSpan');
this.percentSpan = this.shadowRoot.getElementById('percentSpan');
this.diffSpan = this.shadowRoot.getElementById('diffSpan');
this.historySpan = this.shadowRoot.getElementById('history');
this.searchInput = this.shadowRoot.getElementById('searchInput');
this.pid = this.shadowRoot.getElementById('pid');
this.tid = this.shadowRoot.getElementById('tid');
this.processName = this.shadowRoot.getElementById('processName');
this.threadName = this.shadowRoot.getElementById('threadName');
this.sample = this.shadowRoot.getElementById('sample');
this.titleDiv = this.shadowRoot.getElementById('title');
this.context = this.panel.getContext('2d');
this.panel.width = this.shadowRoot.host.clientWidth;
this.panel.height = this.shadowRoot.host.clientHeight;
this.historySpan.onclick = e => {
if (this.history.length > 2) {
this.history.pop();
this.historyRefer.pop();
this.zoomOutRefer(this.historyRefer[this.historyRefer.length - 1])
this.zoomOut(this.history[this.history.length - 1])
} else if (this.history.length == 2) {
this.history.pop();
this.historyRefer.pop();
this.zoomOutRefer(this.historyRefer[this.historyRefer.length - 1])
this.zoomOut(this.history[this.history.length - 1])
this.historySpan.style.display = 'none';
} else {
this.historySpan.style.display = 'none';
}
}
this.searchInput.addEventListener("onPressEnter", (e) => {
this.keyword = e.currentTarget.value;
requestAnimationFrame(this.draw);
})
this.searchInput.addEventListener('onClear', e => {
this.keyword = null;
requestAnimationFrame(this.draw);
})
this.panel.onmouseover = (e) => {
this.mouseState = 'mouseOver';
}
this.panel.onmouseleave = e => {
this.mouseState = 'mouseLeave';
this.mouseX = 0;
this.mouseY = 0;
requestAnimationFrame(this.draw)
}
this.panel.onmousemove = e => {
const pos = e.currentTarget.getBoundingClientRect();
this.mouseX = e.clientX - pos.left;
this.mouseY = e.clientY - pos.top;
this.mouseState = 'mouseMove';
requestAnimationFrame(this.draw)
}
this.panel.onmousedown = e => {
this.mouseState = 'mouseDown';
}
this.panel.onmouseup = e => {
this.mouseState = 'mouseUp';
const pos = e.currentTarget.getBoundingClientRect();
this.mouseX = e.clientX - pos.left;
this.mouseY = e.clientY - pos.top;
requestAnimationFrame(this.draw)
}
}
set data(value) {
this._data = value;
this._dataRefer = value.jsonRefer;
* type 值默认为1
* 1 Show percentage of event count relative to the current thread
* 2 Show percentage of event count relative to the current process
* 3 Show percentage of event count relative to all process
* 4 show event count
* 5 show event count in milliseconds
* 其他值
* pid, processName, tid, threadName, eventCount, sampleCount, CallOrder(g节点)
*/
this.type = value.type;
this.reverse = value.reverse || false;
if (value.CallOrder.symbol === -1) {
this._c = value.CallOrder.callStack;
this._cRef = this.threadRefer.CallOrder.callStack;
} else {
this._c = [value.CallOrder];
this._cRef = [this.threadRefer.CallOrder];
}
this.history.push(this._c)
this.historyRefer.push(this.cRef)
this.eventCountAllProcess = this._data.json.recordSampleInfo[window.eventIndex].eventCount
console.log("diff eventCountAllProcess = " + this.eventCountAllProcess)
if (value.pid) {
this.eventCountCurrentProcess = this._data.json.recordSampleInfo[window.eventIndex].processes.filter(it => it.pid === value.pid)[0].eventCount
console.log("diff eventCountCurrentProcess = " + this.eventCountCurrentProcess)
this.pid.textContent = value.pid;
} else {
}
if (value.tid) {
this.eventCountCurrentThread = this._data.json.recordSampleInfo[window.eventIndex].processes.filter(it => it.pid === value.pid)[0].threads.filter(it => it.tid === value.tid)[0].eventCount;
this.eventCountCurrentThreadRefer = this._data.jsonRefer.recordSampleInfo[window.eventIndex].processes.filter(it => it.pid === value.pid)[0].threads.filter(it => it.tid === value.tid)[0].eventCount;
console.log("diff eventCountCurrentThread = " + this.eventCountCurrentThread)
this.tid.textContent = value.tid;
}
if (value.sampleCount) {
this.sample.textContent = ' (Samples: ' + value.sampleCount + ")";
}
if (value.processName) {
this.processName.textContent = value.processName ? `(${value.processName})` : '';
}
if (value.threadName) {
this.threadName.textContent = value.threadName ? `(${value.threadName})` : '';
}
if (value.funcName) {
this.titleDiv.innerHTML = `${value.funcName}`
this.controller.style.top = `${this.titleDiv.clientHeight + 10}px`
}
this.maxDepth = this.getMaxDepth(this.data.CallOrder.callStack) + 5;
this.maxDepthRefer = this.getMaxDepth(this.threadRefer.CallOrder.callStack) + 5;
console.log("diff maxDepth = " + this.maxDepth + " maxDepthRefer = " + this.maxDepthRefer)
this.sumCount = this.data.CallOrder.subEvents;
this.sumCountRefer = this.threadRefer.CallOrder.subEvents;
console.log("diff sumCount = " + this.sumCount + " sumCountRefer = " + this.sumCountRefer)
this.panel.height = this.maxDepth * this.rowHeight
this.panel.width = this.shadowRoot.host.clientWidth
this.makeHighRes(this.panel);
requestAnimationFrame(this.draw);
}
get data() {
return this._data;
}
get dataRefer() {
return this._dataRefer;
}
set dataRefer(val) {
this._dataRefer = val;
}
获取要对比的 thread数据
*/
get threadRefer() {
return this._threadRefer;
}
* 设置要对比的数据 这里传入的是 thread 节点;包含 {tid,eventCount,sampleCount,libs:[],CallOrder:[],CalledOrder:[]}
* @param val
*/
set threadRefer(val) {
this._threadRefer = val;
}
* 点击图形中子节点时 传入 当前节点当作根节点绘制
* @param value
*/
set c(value) {
this.historySpan.style.display = 'block';
this._c = value;
this.history.push(this._c)
requestAnimationFrame(this.draw);
}
get c() {
return this._c;
}
get cRef() {
return this._cRef
}
set cRef(val) {
this._cRef = val;
this.historyRefer.push(this._cRef)
}
zoomOutRefer(value) {
this._cRef = value;
}
zoomOut(value) {
this._c = value;
requestAnimationFrame(this.draw);
}
makeHighRes(canvas) {
let ctx = canvas.getContext('2d');
let dpr = window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1;
let oldWidth = canvas.width;
let oldHeight = canvas.height;
canvas.width = Math.round(oldWidth * dpr);
canvas.height = Math.round(oldHeight * dpr);
canvas.style.width = oldWidth + 'px';
canvas.style.height = oldHeight + 'px';
ctx.scale(dpr, dpr);
this.context = ctx;
return ctx;
}
draw = () => {
let ctx = this.context;
let grad = ctx.createLinearGradient(0, 0, 0, this.panel.height / 2);
grad.addColorStop(0, "#eeeeee");
grad.addColorStop(1, "#efefb1");
ctx.fillStyle = grad;
ctx.fillRect(0, 0, this.panel.width, this.panel.height);
if (this.data) {
if (this.reverse) {
this.drawCReverse(
this.c,
1,
{
x: 0,
y: this.rowHeight * 4,
w: this.panel.clientWidth,
h: this.rowHeight
});
} else {
this.drawC(
this.c,
this.cRef,
1,
{
x: 0,
y: this.panel.clientHeight - this.rowHeight,
w: this.panel.clientWidth,
h: this.rowHeight
});
}
}
}
getStatistics(c) {
let statistics;
switch (this.type) {
case 1:
statistics = c.subEvents * 100 / this.eventCountCurrentThread;
statistics = statistics.toFixed(2)
statistics = `${statistics}%`
break;
case 2:
statistics = c.subEvents * 100 / this.eventCountCurrentProcess;
statistics = statistics.toFixed(2)
statistics = `${statistics}%`
break;
case 3:
statistics = c.subEvents * 100 / this.eventCountAllProcess;
statistics = statistics.toFixed(2)
statistics = `${statistics}%`
break;
case 4:
statistics = c.subEvents;
statistics = `${statistics}`
break;
case 5:
statistics = c.subEvents / 1000000;
statistics = statistics.toFixed(3)
statistics = `${statistics} ms`
break;
default:
statistics = c.subEvents * 100 / this.eventCountCurrentThread;
statistics = statistics.toFixed(2)
statistics = `${statistics}%`
break;
}
return statistics;
}
htmlDecode(text) {
let temp = document.createElement("div");
temp.innerHTML = text;
let output = temp.innerText || temp.textContent;
temp = null;
return output;
}
getFunctionName(f) {
let funName = "";
if (this._data.json.SymbolMap[f]) {
funName = this._data.json.SymbolMap[f].symbol;
} else {
let f = c[i].symbol;
console.log(`processId:${this.pid.textContent} processName:${this.processName.textContent} threadId:${this.tid.textContent} threadName:${this.threadName.textContent}`, c[i], "SymbolMap中没有对应的值")
}
return this.htmlDecode(funName);
}
getFunctionNameRefer(f) {
let funName = "";
if (this.dataRefer.SymbolMap[f]) {
funName = this.dataRefer.SymbolMap[f].symbol;
} else {
}
return this.htmlDecode(funName);
}
getColor(percent2, funName) {
let heatColor;
if (this.keyword && this.keyword.length > 0 && funName.indexOf(this.keyword) != -1) {
heatColor = { r: 0x66, g: 0xad, b: 0xff };
} else {
heatColor = this.getHeatColor(percent2);
}
return heatColor;
}
drawCReverse = (c, dept, rect) => {
let ctx = this.context;
let offset = 0
for (let i = 0; i < c.length; i++) {
let funName = this.getFunctionName(c[i].symbol);
let funcId = c[i].symbol;
let percent = c[i].subEvents * 100 / (c.reduce((acc, cur) => acc + cur.subEvents, 0));
let percent2 = c[i].subEvents * 100 / this.sumCount;
if (percent2 < 0.1) continue
let heatColor = this.getColor(percent2, funName);
let w = rect.w * (percent / 100.0);
if (w < 1) {
w = 1
}
let _x = rect.x + offset;
ctx.fillStyle = `rgba(${heatColor.r}, ${heatColor.g}, ${heatColor.b}, 1)`;
ctx.fillRect(_x, rect.y + 2, w, rect.h - 2);
ctx.fillStyle = "rgba(0,0,0,1)";
let txtWidth = ctx.measureText(funName).width;
let chartWidth = txtWidth / funName.length;
let number = (w - 6) / chartWidth;
if (number >= 4 && number < funName.length - 3) {
ctx.fillText(funName.slice(0, number - 3) + '...', _x + 3, rect.y + 13, w - 6)
} else if (number >= 4) {
ctx.fillText(funName, _x + 3, rect.y + 13, w - 6)
}
let _rect = {
x: _x, y: rect.y, w: w, h: rect.h
}
c[i].rect = _rect;
if (this.mouseX > _x && this.mouseX < _x + w && this.mouseY > rect.h * 3 + (dept) * rect.h && this.mouseY < rect.h * 3 + (dept + 1) * rect.h) {
if (this.mouseState === 'mouseMove') {
ctx.lineWidth = 2;
ctx.strokeStyle = `#000000`;
ctx.strokeRect(_x, rect.y + 1, w - 1, rect.h);
this.funcNameSpan.textContent = funName
this.panel.title = funName
this.percentSpan.textContent = this.getStatistics(c[i]);
} else {
if (this.mouseState === 'mouseUp') {
this.mouseState = null;
if (!this.compareNodes(this.c, [c[i]])) {
this.c = [c[i]];
}
}
}
} else {
ctx.lineWidth = 1;
}
offset += w;
if (c[i].callStack && c[i].callStack.length > 0) {
_rect.y = _rect.y + _rect.h
this.drawCReverse(c[i].callStack, dept + 1, _rect);
}
}
}
drawC = (c, cRef, dept, rect) => {
let ctx = this.context;
let offset = 0
for (let i = 0; i < c.length; i++) {
let funName = this.getFunctionName(c[i].symbol);
let funcId = c[i].symbol;
let percent = c[i].subEvents * 100 / (c.reduce((acc, cur) => acc + cur.subEvents, 0));
let percent2 = c[i].subEvents * 100 / this.sumCount;
if (percent2 < 0.1) continue
let heatColor = this.getColor(percent2, funName);
let w = rect.w * (percent / 100.0);
if (w < 1) {
w = 1
}
let _x = rect.x + offset;
ctx.fillStyle = `rgba(${heatColor.r}, ${heatColor.g}, ${heatColor.b}, 1)`;
let __rect = { x: _x, y: rect.y + 2, w: w, h: rect.h - 2 }
ctx.fillRect(__rect.x, __rect.y, __rect.w, __rect.h);
let nodeRefer = this.drawReferData(ctx, funName, __rect, percent, cRef);
ctx.fillStyle = "rgba(0,0,0,1)";
let txtWidth = ctx.measureText(funName).width;
let chartWidth = txtWidth / funName.length;
let number = (w - 6) / chartWidth;
if (number >= 4 && number < funName.length - 3) {
ctx.fillText(funName.slice(0, number - 3) + '...', _x + 3, rect.y + 13, w - 6)
} else if (number >= 4) {
ctx.fillText(funName, _x + 3, rect.y + 13, w - 6)
}
let _rect = {
x: _x, y: rect.y, w: w, h: rect.h
}
c[i].rect = _rect;
if (this.mouseX > _x && this.mouseX < _x + w && this.mouseY > (this.maxDepth - dept) * rect.h && this.mouseY < (this.maxDepth - dept + 1) * rect.h) {
if (this.mouseState === 'mouseMove') {
ctx.lineWidth = 2;
ctx.strokeStyle = `#000000`;
ctx.strokeRect(_x, rect.y + 1, w - 1, rect.h);
this.funcNameSpan.textContent = funName
this.panel.title = funName
this.percentSpan.textContent = this.getStatistics(c[i]);
let node = this.fetchSameFuncNameData(funName, cRef);
var diff = 0.0;
if (node) {
let percentRefer = node.subEvents * 100 / (cRef.reduce((acc, cur) => acc + cur.subEvents, 0))
diff = (percent - percentRefer).toFixed(2)
}
this.diffSpan.textContent = `diff: ${diff}%`;
} else {
if (this.mouseState === 'mouseUp') {
this.mouseState = null;
if (!this.compareNodes(this.c, [c[i]])) {
this.c = [c[i]];
this.cRef = [nodeRefer];
}
}
}
} else {
ctx.lineWidth = 1;
}
offset += w;
if (c[i].callStack && c[i].callStack.length > 0) {
_rect.y = _rect.y - _rect.h
this.drawC(c[i].callStack, nodeRefer ? nodeRefer.callStack : [], dept + 1, _rect);
}
}
}
fetchSameFuncNameData(funName, cRef) {
let node = cRef.filter(it => {
let funNameRefer = this.getFunctionNameRefer(it.symbol);
return funName === funNameRefer
})
if (node && node.length > 0) {
return node[0];
} else {
return null;
}
}
drawReferData(ctx, funName, rect, percent, cRef) {
let node = this.fetchSameFuncNameData(funName, cRef);
if (node) {
let percentRefer = node.subEvents * 100 / (cRef.reduce((acc, cur) => acc + cur.subEvents, 0));
if (percent < percentRefer) {
let offset = Math.abs(percent - percentRefer);
if (offset > 0 && offset <= 10) {
ctx.fillStyle = `#66B3FF`;
} else if (offset > 10 && offset <= 20) {
ctx.fillStyle = `#2894FF`;
} else if (offset > 20 && offset <= 30) {
ctx.fillStyle = `#0072E3`;
} else if (offset > 30 && offset <= 50) {
ctx.fillStyle = `#0066CC`;
} else {
ctx.fillStyle = `#005AB5`;
}
ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
} else if (percent > percentRefer) {
let offset = Math.abs(percent - percentRefer);
if (offset > 0 && offset <= 10) {
ctx.fillStyle = `#FF7575`;
} else if (offset > 10 && offset <= 20) {
ctx.fillStyle = `#FF2D2D`;
} else if (offset > 20 && offset <= 30) {
ctx.fillStyle = `#EA0000`;
} else if (offset > 30 && offset <= 50) {
ctx.fillStyle = `#CE0000`;
} else {
ctx.fillStyle = `#AE0000`;
}
ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
}
}
return node;
}
compareNode(na, nb) {
let res = false;
if (na.selfEvents === nb.selfEvents && na.subEvents === nb.subEvents && na.symbol === nb.symbol) {
res = this.compareNodes(na.callStack || [], nb.callStack || []);
}
return res;
}
compareNodes(a, b) {
let res = false;
if (a.length === b.length) {
if (a.length === 0) {
return true;
}
for (let i = 0; i < a.length; i++) {
res = this.compareNode(a[i], b[i])
}
}
return res;
}
getMaxDepth(nodes) {
let isArray = Array.isArray(nodes);
let sumCount;
if (isArray) {
sumCount = nodes.reduce((acc, cur) => acc + cur.subEvents, 0);
} else {
sumCount = nodes.subEvents;
}
let width = sumCount * 100.0 / this.sumCount;
if (width < 0.1) {
return 0;
}
let children = isArray ? this.splitChildrenForNodes(nodes) : nodes.callStack;
let childDepth = 0;
if (children) {
for (let child of children) {
childDepth = Math.max(childDepth, this.getMaxDepth(child));
}
}
return childDepth + 1;
}
splitChildrenForNodes(nodes) {
let map = new Map();
for (let node of nodes) {
for (let child of node.callStack) {
let subNodes = map.get(child.symbol);
if (subNodes) {
subNodes.push(child);
} else {
map.set(child.symbol, [child]);
}
}
}
let res = [];
for (let subNodes of map.values()) {
res.push(subNodes.length == 1 ? subNodes[0] : subNodes);
}
return res;
}
getHeatColor(widthPercentage) {
return {
r: Math.floor(245 + 10 * (1 - widthPercentage * 0.01)),
g: Math.floor(110 + 105 * (1 - widthPercentage * 0.01)),
b: 100,
};
}
attributeChangedCallback(name, oldValue, newValue) {
}
}
if (!customElements.get('app-diff-flame')) {
customElements.define('app-diff-flame', AppDiffFlame);
}
class AppNormalFlame extends HTMLElement {
draw;
drawC;
rowHeight = 17;
static get observedAttributes() {
return []
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
font-size:inherit;
display:inline-flex;
align-items: center;
justify-content:center;
height: 100%;
width: 100%;
margin-bottom: 40px;
}
canvas { border: 1px solid #e9e9e9; }
#title{
font-weight: bold;
height: auto;
}
#title span{
color: gray;
}
#funcNameSpan{
display: inline-block;
border: 1px solid #e9e9e9;
box-sizing: border-box;
border-radius: 2px;
padding: 2px 8px;
background: #fff;
color: gray;
flex: 3;
margin-left: 10px;
}
#percentSpan{
display: inline-block;
border: 1px solid #e9e9e9;
margin-left: 10px;
box-sizing: border-box;
border-radius: 2px;
padding: 2px 8px;
background: #fff;
min-width: 160px;
max-width: 160px;
margin-left: 10px;
width: 100px;
height: 30px;
color: gray;
}
#history{
display: none;
border: 1px solid #42b983;
box-sizing: border-box;
border-radius: 2px;
padding: 2px 8px;
background: #fff;
width: 100px;
margin-left: 10px;
height: 30px;
cursor: pointer;
user-select: none;
}
#history:hover{
background: #42b983;
color: #fff;
}
#searchInput{
height: 30px;
margin-left: 10px;
margin-right: 10px;
flex: 1;
}
</style>
<div style="position: relative;width: 100%">
<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>
<canvas id="panel" title=""></canvas>
<div id="controller" style="position: absolute;top: 32px;display: flex;width: 100%">
<span id="history">Zoom Out</span>
<span id="funcNameSpan"></span>
<span id="percentSpan"></span>
<lit-input id="searchInput" placeholder="search" allow-clear>Search</lit-input>
</div>
</div>
<slot></slot>
`
}
connectedCallback() {
this.history = [];
this.panel = this.shadowRoot.getElementById('panel');
this.controller = this.shadowRoot.getElementById('controller');
this.funcNameSpan = this.shadowRoot.getElementById('funcNameSpan');
this.percentSpan = this.shadowRoot.getElementById('percentSpan');
this.historySpan = this.shadowRoot.getElementById('history');
this.searchInput = this.shadowRoot.getElementById('searchInput');
this.pid = this.shadowRoot.getElementById('pid');
this.tid = this.shadowRoot.getElementById('tid');
this.processName = this.shadowRoot.getElementById('processName');
this.threadName = this.shadowRoot.getElementById('threadName');
this.sample = this.shadowRoot.getElementById('sample');
this.titleDiv = this.shadowRoot.getElementById('title');
this.context = this.panel.getContext('2d');
this.panel.width = this.shadowRoot.host.clientWidth;
this.panel.height = this.shadowRoot.host.clientHeight;
this.historySpan.onclick = e => {
if (this.history.length > 2) {
this.history.pop();
this.zoomOut(this.history[this.history.length - 1])
} else if (this.history.length == 2) {
this.history.pop();
this.zoomOut(this.history[this.history.length - 1])
this.historySpan.style.display = 'none';
} else {
this.historySpan.style.display = 'none';
}
}
this.searchInput.addEventListener("onPressEnter", (e) => {
this.keyword = e.currentTarget.value;
requestAnimationFrame(this.draw);
})
this.searchInput.addEventListener('onClear', e => {
this.keyword = null;
requestAnimationFrame(this.draw);
})
this.panel.onmouseover = (e) => {
this.mouseState = 'mouseOver';
}
this.panel.onmouseleave = e => {
this.mouseState = 'mouseLeave';
this.mouseX = 0;
this.mouseY = 0;
requestAnimationFrame(this.draw)
}
this.panel.onmousemove = e => {
const pos = e.currentTarget.getBoundingClientRect();
this.mouseX = e.clientX - pos.left;
this.mouseY = e.clientY - pos.top;
this.mouseState = 'mouseMove';
requestAnimationFrame(this.draw)
}
this.panel.onmousedown = e => {
this.mouseState = 'mouseDown';
}
this.panel.onmouseup = e => {
this.mouseState = 'mouseUp';
const pos = e.currentTarget.getBoundingClientRect();
this.mouseX = e.clientX - pos.left;
this.mouseY = e.clientY - pos.top;
requestAnimationFrame(this.draw)
}
}
set data(value) {
this._data = value;
this.type = value.type;
this.reverse = value.reverse || false;
if (value.CallOrder.symbol === -1) {
this._c = value.CallOrder.callStack;
} else {
this._c = [value.CallOrder];
}
this.history.push(this._c)
this.eventCountAllProcess = value.json.recordSampleInfo[window.eventIndex].eventCount
if (value.pid) {
this.eventCountCurrentProcess = value.json.recordSampleInfo[window.eventIndex].processes.filter(it => it.pid === value.pid)[0].eventCount
this.pid.textContent = value.pid;
} else {
}
if (value.tid) {
this.eventCountCurrentThread = value.json.recordSampleInfo[window.eventIndex].processes.filter(it => it.pid === value.pid)[0].threads.filter(it => it.tid === value.tid)[0].eventCount;
this.tid.textContent = value.tid;
}
if (value.sampleCount) {
this.sample.textContent = ' (Samples: ' + value.sampleCount + ")";
}
if (value.processName) {
this.processName.textContent = value.processName ? `(${value.processName})` : '';
}
if (value.threadName) {
this.threadName.textContent = value.threadName ? `(${value.threadName})` : '';
}
if (value.funcName) {
this.titleDiv.innerHTML = `${value.funcName}`
this.controller.style.top = `${this.titleDiv.clientHeight + 10}px`
}
this.maxDepth = this.getMaxDepth(this.data.CallOrder.callStack) + 5;
this.sumCount = this.data.CallOrder.subEvents;
this.panel.height = this.maxDepth * this.rowHeight
this.panel.width = this.shadowRoot.host.clientWidth
this.makeHighRes(this.panel);
requestAnimationFrame(this.draw);
}
get data() {
return this._data;
}
set c(value) {
this.historySpan.style.display = 'block';
this._c = value;
this.history.push(this._c)
requestAnimationFrame(this.draw);
}
get c() {
return this._c;
}
zoomOut(value) {
this._c = value;
requestAnimationFrame(this.draw);
}
makeHighRes(canvas) {
let ctx = canvas.getContext('2d');
let dpr = window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1;
let oldWidth = canvas.width;
let oldHeight = canvas.height;
canvas.width = Math.round(oldWidth * dpr);
canvas.height = Math.round(oldHeight * dpr);
canvas.style.width = oldWidth + 'px';
canvas.style.height = oldHeight + 'px';
ctx.scale(dpr, dpr);
this.context = ctx;
return ctx;
}
draw = () => {
let ctx = this.context;
let grad = ctx.createLinearGradient(0, 0, 0, this.panel.height / 2);
grad.addColorStop(0, "#eeeeee");
grad.addColorStop(1, "#efefb1");
ctx.fillStyle = grad;
ctx.fillRect(0, 0, this.panel.width, this.panel.height);
if (this.data) {
if (this.reverse) {
this.drawCReverse(
this.c,
1,
{
x: 0,
y: this.rowHeight * 4,
w: this.panel.clientWidth,
h: this.rowHeight
});
} else {
this.drawC(
this.c,
1,
{
x: 0,
y: this.panel.clientHeight - this.rowHeight,
w: this.panel.clientWidth,
h: this.rowHeight
});
}
}
}
getStatistics(c) {
let statistics;
switch (this.type) {
case 1:
statistics = c.subEvents * 100 / this.eventCountCurrentThread;
statistics = statistics.toFixed(2)
statistics = `${statistics}%`
break;
case 2:
statistics = c.subEvents * 100 / this.eventCountCurrentProcess;
statistics = statistics.toFixed(2)
statistics = `${statistics}%`
break;
case 3:
statistics = c.subEvents * 100 / this.eventCountAllProcess;
statistics = statistics.toFixed(2)
statistics = `${statistics}%`
break;
case 4:
statistics = c.subEvents;
statistics = `${statistics}`
break;
case 5:
statistics = c.subEvents / 1000000;
statistics = statistics.toFixed(3)
statistics = `${statistics} ms`
break;
default:
statistics = c.subEvents * 100 / this.eventCountCurrentThread;
statistics = statistics.toFixed(2)
statistics = `${statistics}%`
break;
}
return statistics;
}
htmlDecode(text) {
let temp = document.createElement("div");
temp.innerHTML = text;
let output = temp.innerText || temp.textContent;
temp = null;
return output;
}
getFunctionName(f) {
let funName = "";
if (this.data.json.SymbolMap[f]) {
funName = this.data.json.SymbolMap[f].symbol;
} else {
let f = c[i].symbol;
console.log(`processId:${this.pid.textContent} processName:${this.processName.textContent} threadId:${this.tid.textContent} threadName:${this.threadName.textContent}`, c[i], "SymbolMap中没有对应的值")
}
return this.htmlDecode(funName);
}
getColor(percent2, funName) {
let heatColor;
if (this.keyword && this.keyword.length > 0 && funName.indexOf(this.keyword) != -1) {
heatColor = { r: 0x66, g: 0xad, b: 0xff };
} else {
heatColor = this.getHeatColor(percent2);
}
return heatColor;
}
drawCReverse = (c, dept, rect) => {
let ctx = this.context;
let offset = 0
for (let i = 0; i < c.length; i++) {
let funName = this.getFunctionName(c[i].symbol);
let funcId = c[i].symbol;
let percent = c[i].subEvents * 100 / (c.reduce((acc, cur) => acc + cur.subEvents, 0));
let percent2 = c[i].subEvents * 100 / this.sumCount;
if (percent2 < 0.1) continue
let heatColor = this.getColor(percent2, funName);
let w = rect.w * (percent / 100.0);
if (w < 1) {
w = 1
}
let _x = rect.x + offset;
ctx.fillStyle = `rgba(${heatColor.r}, ${heatColor.g}, ${heatColor.b}, 1)`;
ctx.fillRect(_x, rect.y + 2, w, rect.h - 2);
ctx.fillStyle = "rgba(0,0,0,1)";
let txtWidth = ctx.measureText(funName).width;
let chartWidth = txtWidth / funName.length;
let number = (w - 6) / chartWidth;
if (number >= 4 && number < funName.length - 3) {
ctx.fillText(funName.slice(0, number - 3) + '...', _x + 3, rect.y + 13, w - 6)
} else if (number >= 4) {
ctx.fillText(funName, _x + 3, rect.y + 13, w - 6)
}
let _rect = {
x: _x, y: rect.y, w: w, h: rect.h
}
c[i].rect = _rect;
if (this.mouseX > _x && this.mouseX < _x + w && this.mouseY > rect.h * 3 + (dept) * rect.h && this.mouseY < rect.h * 3 + (dept + 1) * rect.h) {
if (this.mouseState === 'mouseMove') {
ctx.lineWidth = 2;
ctx.strokeStyle = `#000000`;
ctx.strokeRect(_x, rect.y + 1, w - 1, rect.h);
this.funcNameSpan.textContent = funName
this.panel.title = funName
this.percentSpan.textContent = this.getStatistics(c[i]);
} else {
if (this.mouseState === 'mouseUp') {
this.mouseState = null;
if (!this.compareNodes(this.c, [c[i]])) {
this.c = [c[i]];
}
}
}
} else {
ctx.lineWidth = 1;
}
offset += w;
if (c[i].callStack && c[i].callStack.length > 0) {
_rect.y = _rect.y + _rect.h
this.drawCReverse(c[i].callStack, dept + 1, _rect);
}
}
}
drawC = (c, dept, rect) => {
let ctx = this.context;
let offset = 0
for (let i = 0; i < c.length; i++) {
let funName = this.getFunctionName(c[i].symbol);
let funcId = c[i].symbol;
let percent = c[i].subEvents * 100 / (c.reduce((acc, cur) => acc + cur.subEvents, 0));
let percent2 = c[i].subEvents * 100 / this.sumCount;
if (percent2 < 0.1) continue
let heatColor = this.getColor(percent2, funName);
let w = rect.w * (percent / 100.0);
if (w < 1) {
w = 1
}
let _x = rect.x + offset;
ctx.fillStyle = `rgba(${heatColor.r}, ${heatColor.g}, ${heatColor.b}, 1)`;
ctx.fillRect(_x, rect.y + 2, w, rect.h - 2);
ctx.fillStyle = "rgba(0,0,0,1)";
let txtWidth = ctx.measureText(funName).width;
let chartWidth = txtWidth / funName.length;
let number = (w - 6) / chartWidth;
if (number >= 4 && number < funName.length - 3) {
ctx.fillText(funName.slice(0, number - 3) + '...', _x + 3, rect.y + 13, w - 6)
} else if (number >= 4) {
ctx.fillText(funName, _x + 3, rect.y + 13, w - 6)
}
let _rect = {
x: _x, y: rect.y, w: w, h: rect.h
}
c[i].rect = _rect;
if (this.mouseX > _x && this.mouseX < _x + w && this.mouseY > (this.maxDepth - dept) * rect.h && this.mouseY < (this.maxDepth - dept + 1) * rect.h) {
if (this.mouseState === 'mouseMove') {
ctx.lineWidth = 2;
ctx.strokeStyle = `#000000`;
ctx.strokeRect(_x, rect.y + 1, w - 1, rect.h);
this.funcNameSpan.textContent = funName
this.panel.title = funName
this.percentSpan.textContent = this.getStatistics(c[i]);
} else {
if (this.mouseState === 'mouseUp') {
this.mouseState = null;
if (!this.compareNodes(this.c, [c[i]])) {
this.c = [c[i]];
}
}
}
} else {
ctx.lineWidth = 1;
}
offset += w;
if (c[i].callStack && c[i].callStack.length > 0) {
_rect.y = _rect.y - _rect.h
this.drawC(c[i].callStack, dept + 1, _rect);
}
}
}
compareNode(na, nb) {
let res = false;
if (na.selfEvents === nb.selfEvents && na.subEvents === nb.subEvents && na.symbol === nb.symbol) {
res = this.compareNodes(na.callStack || [], nb.callStack || []);
}
return res;
}
compareNodes(a, b) {
let res = false;
if (a.length === b.length) {
if (a.length === 0) {
return true;
}
for (let i = 0; i < a.length; i++) {
res = this.compareNode(a[i], b[i])
}
}
return res;
}
getMaxDepth(nodes) {
let isArray = Array.isArray(nodes);
let sumCount;
if (isArray) {
sumCount = nodes.reduce((acc, cur) => acc + cur.subEvents, 0);
} else {
sumCount = nodes.subEvents;
}
let width = sumCount * 100.0 / this.sumCount;
if (width < 0.1) {
return 0;
}
let children = isArray ? this.splitChildrenForNodes(nodes) : nodes.callStack;
let childDepth = 0;
if (children) {
for (let child of children) {
childDepth = Math.max(childDepth, this.getMaxDepth(child));
}
}
return childDepth + 1;
}
splitChildrenForNodes(nodes) {
let map = new Map();
for (let node of nodes) {
for (let child of node.callStack) {
let subNodes = map.get(child.symbol);
if (subNodes) {
subNodes.push(child);
} else {
map.set(child.symbol, [child]);
}
}
}
let res = [];
for (let subNodes of map.values()) {
res.push(subNodes.length == 1 ? subNodes[0] : subNodes);
}
return res;
}
getHeatColor(widthPercentage) {
return {
r: Math.floor(245 + 10 * (1 - widthPercentage * 0.01)),
g: Math.floor(110 + 105 * (1 - widthPercentage * 0.01)),
b: 100,
};
}
attributeChangedCallback(name, oldValue, newValue) {
}
}
if (!customElements.get('app-normal-flame')) {
customElements.define('app-normal-flame', AppNormalFlame);
}
class AppFlameGraph extends HTMLElement {
static get observedAttributes() {
return ['color', 'size']
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host{
font-size:inherit;
display:inline-flex;
align-items: center;
justify-content:center;
width: 100%;
}
</style>
<div style="width: 100%;display: flex;flex-direction: column">
<lit-select id="typeSelect" default-value="1" mode="single" style="width:40vw;margin-bottom: 10px;align-self: flex-end">
<lit-select-option value="1">Show percentage of event count relative to the current thread</lit-select-option>
<lit-select-option value="2">Show percentage of event count relative to the current process</lit-select-option>
<lit-select-option value="3">Show percentage of event count relative to all process</lit-select-option>
<lit-select-option value="4">show event count</lit-select-option>
<lit-select-option value="5">show event count in milliseconds</lit-select-option>
</lit-select>
<div id="panel" style="width: 100%"></div>
</div>
<slot></slot>
`
}
get dataRefer() {
return this._dataRefer;
}
set dataRefer(val) {
this._dataRefer = val;
}
get data() {
return this._json || null;
}
set data(json) {
if (this.isFinished) {
return;
}
this._json = json;
this.panel = this.shadowRoot.getElementById('panel');
this.panel.innerHTML = '';
let processes = json.recordSampleInfo[window.eventIndex].processes;
let processesRefer;
if (this.dataRefer) {
processesRefer = this.dataRefer.recordSampleInfo[window.eventIndex].processes;
}
processes.slice(0).forEach(it => {
let itRefer
if (processesRefer) {
itRefer = processesRefer.find(element => element.pid == it.pid);
}
it.threads.slice(0).forEach(th => {
let pid = it.pid;
let processName = json.processNameMap[it.pid];
let tid = th.tid;
let threadName = json.threadNameMap[th.tid];
let eventCount = th.eventCount;
let sampleCount = th.sampleCount;
let g = th.CallOrder;
if (this.dataRefer) {
console.log("create diff")
let thRefer;
if (itRefer) {
thRefer = itRefer.threads.find(e => e.tid == th.tid);
}
let chart = document.createElement('app-diff-flame');
chart.style.width = '100%'
chart.style.height = 'auto';
chart.style.display = 'flex'
this.panel.appendChild(chart);
chart.threadRefer = thRefer;
chart.data = {
json: json,
jsonRefer: this.dataRefer,
type: this.type || 1,
pid, processName, tid, threadName, eventCount, sampleCount, CallOrder: g
}
} else {
console.log("create normal")
let chart = document.createElement('app-normal-flame');
chart.style.width = '100%'
chart.style.height = 'auto';
chart.style.display = 'flex'
this.panel.appendChild(chart);
chart.data = {
json: json,
type: this.type || 1,
pid, processName, tid, threadName, eventCount, sampleCount, CallOrder: g
}
}
})
})
this.isFinished = true;
}
connectedCallback() {
this.isFinished = false;
this.panel = this.shadowRoot.getElementById('panel');
this.typeSelect = this.shadowRoot.getElementById('typeSelect');
this.typeSelect.onchange = ev => {
this.type = parseInt(ev.detail.value);
this.isFinished = false;
this.data = this.data;
}
}
attributeChangedCallback(name, oldValue, newValue) {
if (name == 'color' && this.loading) {
this.loading.style.color = newValue;
}
if (name == 'size' && this.loading) {
this.loading.style.fontSize = newValue + 'px';
}
}
}
if (!customElements.get('app-flame-graph')) {
customElements.define('app-flame-graph', AppFlameGraph);
}
(function (values) {
function createPromise(callback) {
if (callback) {
return new Promise((resolve, _) => callback(resolve));
}
return new Promise((resolve, _) => resolve());
}
function initGlobalObjects1() {
let recordData = document.querySelector('#record_data_diff_1').textContent;
if (recordData.trim().length > 0) {
return new Promise((resolve, reject) => {
resolve(JSON.parse(recordData));
})
} else {
return fetch('data-diff-1.json').then(response => response.json())
}
}
function initGlobalObjects2() {
let recordData = document.querySelector('#record_data_diff_2').textContent;
if (recordData.trim().length > 0) {
return new Promise((resolve, reject) => {
resolve(JSON.parse(recordData));
})
} else {
return fetch('data-diff-2.json').then(response => response.json())
}
}
function waitDocumentReady() {
return createPromise((resolve) => document.addEventListener("DOMContentLoaded", resolve));
}
createPromise()
.then(waitDocumentReady)
.then(() => Promise.all([initGlobalObjects1(), initGlobalObjects2()]))
.then((array) => {
let j1 = array[0]
let j2 = array[1]
let eventSelector1 = document.querySelector('#events1')
let changeBaseBt = document.querySelector('#changeBaseBt')
if (j1.recordSampleInfo && j1.recordSampleInfo.length > 0) {
let events = [];
j1.recordSampleInfo.forEach((e, index) => {
events.push({ key: index + '', val: e.eventConfigName })
})
eventSelector1.dataSource = events;
window.eventIndex = 0;
}
let loading = document.querySelector('#loading');
let tabs = document.querySelector('#tabs')
let diffFlame = document.querySelector('#diff-flame');
let flame1 = document.querySelector('#flame-1');
let flame2 = document.querySelector('#flame-2');
let legend1 = document.querySelector('#legend-1');
let legend2 = document.querySelector('#legend-2');
tabs.onTabClick = (e) => {
if (e.detail.key == 1) {
diffFlame.isFinished = false;
diffFlame.dataRefer = window.isReserved ? j1 : j2;
diffFlame.data = window.isReserved ? j2 : j1;
} else if (e.detail.key == 2) {
flame1.isFinished = false;
flame1.data = j1;
} else {
flame2.isFinished = false;
flame2.data = j2;
}
}
window.isReserved = false;
diffFlame.isFinished = false;
diffFlame.dataRefer = j2;
diffFlame.data = j1;
eventSelector1.addEventListener('change', (e) => {
loading.style.display = 'flex'
window.eventIndex = parseInt(e.detail.value);
diffFlame.isFinished = false;
diffFlame.dataRefer = j2;
diffFlame.data = j1;
flame1.isFinished = false;
flame1.data = j1;
flame2.isFinished = false;
flame2.data = j2;
loading.style.display = 'none'
})
changeBaseBt.addEventListener('click', (e) => {
window.isReserved = !window.isReserved
changeBaseBt.setAttribute('enable', 'false');
loading.style.display = 'flex'
legend1.textContent = window.isReserved ? "diff-data-2" : "diff-data-1";
legend2.textContent = window.isReserved ? "diff-data-1" : "diff-data-2";
diffFlame.isFinished = false;
diffFlame.dataRefer = window.isReserved ? j1 : j2;
diffFlame.data = window.isReserved ? j2 : j1;
loading.style.display = 'none'
changeBaseBt.setAttribute('enable', 'true');
})
})
}())
</script>
<div style="width: 100%;height: 100%">
<div style="width: 100%;display: flex;flex-direction: column;align-items: center">
<lit-loading id="loading" size="32" style="display: none"></lit-loading>
</div>
<div style="display: flex;flex-direction: row;align-items: center;padding: 15px;justify-content: space-between">
<div>
<span style="font-weight: bold;margin-right: 10px">Event Type :</span>
<lit-select id="events1" default-value="0" style="width: 400px"></lit-select>
</div>
</div>
<lit-tabs id='tabs' position="top-left" activekey="1" mode="flat">
<lit-tabpane id="pane1" tab="diff-flame" key="1">
<div style="display: flex;flex-direction: row;align-items: center;justify-content: space-between">
<div style="display: flex;flex-direction: row;align-items: center">
<div style="display: flex;flex-direction: row;align-items: center">
<span>图形数据: </span>
<span id="legend-1">data-diff-1</span>
</div>
<div style="display: flex;flex-direction: row;margin-left: 20px;align-items: center">
<span>比较数据: </span>
<span id="legend-2">data-diff-2</span>
</div>
<div id="changeBaseBt" style="background-color: coral;color: white;margin-left: 40px;
padding: 7px 15px 7px 15px;border-radius: 5px;cursor: pointer">change</div>
</div>
<div style="display: flex;flex-direction: row;align-items: center">
<div style="display: flex;flex-direction: row;align-items: center">
<span style="width: 10px;height: 10px;background-color:#2894FF;margin-right: 10px"></span>
<span>函数执行时间相比小于(颜色越深则差值越大)</span>
</div>
<div style="display: flex;flex-direction: row;align-items: center;margin-left: 30px">
<span style="width: 10px;height: 10px;background-color:#FF2D2D;margin-right: 10px"></span>
<span>函数执行时间相比大于(颜色越深则差值越大)</span>
</div>
</div>
</div>
<div style="padding: 20px">
<app-flame-graph id="diff-flame"></app-flame-graph>
</div>
</lit-tabpane>
<lit-tabpane id="pane2" tab="data-1-flame" key="2">
<app-flame-graph id="flame-1"></app-flame-graph>
</lit-tabpane>
<lit-tabpane id="pane3" tab="data-2-flame" key="3">
<app-flame-graph id="flame-2"></app-flame-graph>
</lit-tabpane>
</lit-tabs>
</div>