数据监控页面重做
This commit is contained in:
parent
d53274288a
commit
1452ac2dad
Binary file not shown.
106
XNSimHtml/components/FloatingChartWindow.css
Normal file
106
XNSimHtml/components/FloatingChartWindow.css
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
.floating-window {
|
||||||
|
position: fixed;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1000;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-window.minimized {
|
||||||
|
transform: translateY(calc(100% - 40px));
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-header {
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding: 8px 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
cursor: move;
|
||||||
|
user-select: none;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-control-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 4px;
|
||||||
|
line-height: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-control-button:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimize-button:hover {
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button:hover {
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px;
|
||||||
|
position: relative;
|
||||||
|
min-height: 200px;
|
||||||
|
transition: height 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-content.minimized {
|
||||||
|
height: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-content canvas {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
cursor: se-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-right: 2px solid #ccc;
|
||||||
|
border-bottom: 2px solid #ccc;
|
||||||
|
}
|
570
XNSimHtml/components/FloatingChartWindow.js
Normal file
570
XNSimHtml/components/FloatingChartWindow.js
Normal file
@ -0,0 +1,570 @@
|
|||||||
|
// 确保 Chart.js 已加载
|
||||||
|
if (!window.Chart) {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = '../chart.min.js';
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
class FloatingChartWindow extends HTMLElement {
|
||||||
|
// 添加静态计数器
|
||||||
|
static zIndexCounter = 1;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({ mode: 'open' });
|
||||||
|
|
||||||
|
// 初始化状态
|
||||||
|
this.position = { x: 100, y: 100 };
|
||||||
|
this.size = { width: 400, height: 300 };
|
||||||
|
this.isDragging = false;
|
||||||
|
this.dragOffset = { x: 0, y: 0 };
|
||||||
|
this.isResizing = false;
|
||||||
|
this.resizeStart = { x: 0, y: 0 };
|
||||||
|
this.isMinimized = false;
|
||||||
|
this.chartInstance = null;
|
||||||
|
this.zIndex = FloatingChartWindow.zIndexCounter++;
|
||||||
|
|
||||||
|
// 存储初始属性值
|
||||||
|
this._title = this.getAttribute('title') || '图表窗口';
|
||||||
|
this._initialPosition = this.getAttribute('initial-position') ?
|
||||||
|
JSON.parse(this.getAttribute('initial-position')) : { x: 100, y: 100 };
|
||||||
|
this._initialSize = this.getAttribute('initial-size') ?
|
||||||
|
JSON.parse(this.getAttribute('initial-size')) : { width: 400, height: 300 };
|
||||||
|
this._chartData = this.getAttribute('chart-data') ?
|
||||||
|
JSON.parse(this.getAttribute('chart-data')) : { labels: [], datasets: [] };
|
||||||
|
this._chartOptions = this.getAttribute('chart-options') ?
|
||||||
|
JSON.parse(this.getAttribute('chart-options')) : {};
|
||||||
|
|
||||||
|
// 绑定方法
|
||||||
|
this.handleMouseDown = this.handleMouseDown.bind(this);
|
||||||
|
this.handleMouseMove = this.handleMouseMove.bind(this);
|
||||||
|
this.handleMouseUp = this.handleMouseUp.bind(this);
|
||||||
|
this.handleResizeStart = this.handleResizeStart.bind(this);
|
||||||
|
this.handleResize = this.handleResize.bind(this);
|
||||||
|
this.handleResizeEnd = this.handleResizeEnd.bind(this);
|
||||||
|
this.handleMinimize = this.handleMinimize.bind(this);
|
||||||
|
this.handleClose = this.handleClose.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['title', 'initial-position', 'initial-size', 'chart-data', 'chart-options', 'z-index'];
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.render();
|
||||||
|
this.initChart();
|
||||||
|
this.addEventListeners();
|
||||||
|
|
||||||
|
// 应用初始属性值
|
||||||
|
this.updateTitle(this._title);
|
||||||
|
this.position = this._initialPosition;
|
||||||
|
this.size = this._initialSize;
|
||||||
|
this.updatePosition();
|
||||||
|
this.updateSize();
|
||||||
|
this.updateChartData(this._chartData);
|
||||||
|
this.updateChartOptions(this._chartOptions);
|
||||||
|
this.updateZIndex(this.zIndex);
|
||||||
|
|
||||||
|
// 触发 connected 事件
|
||||||
|
this.dispatchEvent(new CustomEvent('connected'));
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
this.removeEventListeners();
|
||||||
|
if (this.chartInstance) {
|
||||||
|
this.chartInstance.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
if (oldValue === newValue) return;
|
||||||
|
|
||||||
|
// 存储新的属性值
|
||||||
|
switch (name) {
|
||||||
|
case 'title':
|
||||||
|
this._title = newValue;
|
||||||
|
break;
|
||||||
|
case 'initial-position':
|
||||||
|
this._initialPosition = JSON.parse(newValue);
|
||||||
|
break;
|
||||||
|
case 'initial-size':
|
||||||
|
this._initialSize = JSON.parse(newValue);
|
||||||
|
break;
|
||||||
|
case 'chart-data':
|
||||||
|
this._chartData = JSON.parse(newValue);
|
||||||
|
break;
|
||||||
|
case 'chart-options':
|
||||||
|
this._chartOptions = JSON.parse(newValue);
|
||||||
|
break;
|
||||||
|
case 'z-index':
|
||||||
|
this.zIndex = parseInt(newValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果组件已经渲染,则更新相应的值
|
||||||
|
if (this.shadowRoot) {
|
||||||
|
switch (name) {
|
||||||
|
case 'title':
|
||||||
|
this.updateTitle(this._title);
|
||||||
|
break;
|
||||||
|
case 'initial-position':
|
||||||
|
this.position = this._initialPosition;
|
||||||
|
this.updatePosition();
|
||||||
|
break;
|
||||||
|
case 'initial-size':
|
||||||
|
this.size = this._initialSize;
|
||||||
|
this.updateSize();
|
||||||
|
break;
|
||||||
|
case 'chart-data':
|
||||||
|
this.updateChartData(this._chartData);
|
||||||
|
break;
|
||||||
|
case 'chart-options':
|
||||||
|
this.updateChartOptions(this._chartOptions);
|
||||||
|
break;
|
||||||
|
case 'z-index':
|
||||||
|
this.updateZIndex(this.zIndex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.textContent = `
|
||||||
|
.floating-window {
|
||||||
|
position: fixed;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-header {
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding: 8px 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
cursor: move;
|
||||||
|
user-select: none;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #262626;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-control-button {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-control-button:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
color: #262626;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-control-button.close-button:hover {
|
||||||
|
background: #ff4d4f;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px;
|
||||||
|
position: relative;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-content.minimized {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
cursor: se-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 2px;
|
||||||
|
bottom: 2px;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-right: 2px solid #ccc;
|
||||||
|
border-bottom: 2px solid #ccc;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const template = document.createElement('template');
|
||||||
|
template.innerHTML = `
|
||||||
|
<div class="floating-window">
|
||||||
|
<div class="window-header">
|
||||||
|
<span class="window-title">${this.getAttribute('title') || '图表窗口'}</span>
|
||||||
|
<div class="window-controls">
|
||||||
|
<button class="window-control-button minimize-button">−</button>
|
||||||
|
<button class="window-control-button close-button">×</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="window-content">
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="resize-handle"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.shadowRoot.appendChild(style);
|
||||||
|
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
||||||
|
|
||||||
|
// 更新位置和大小
|
||||||
|
this.updatePosition();
|
||||||
|
this.updateSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
initChart() {
|
||||||
|
const canvas = this.shadowRoot.querySelector('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
const chartData = JSON.parse(this.getAttribute('chart-data') || '{"labels":[],"datasets":[]}');
|
||||||
|
const chartOptions = JSON.parse(this.getAttribute('chart-options') || '{}');
|
||||||
|
|
||||||
|
// 基础配置
|
||||||
|
const baseOptions = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
animation: false,
|
||||||
|
elements: {
|
||||||
|
point: {
|
||||||
|
radius: 0
|
||||||
|
},
|
||||||
|
line: {
|
||||||
|
tension: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: false,
|
||||||
|
ticks: {
|
||||||
|
callback: function(value) {
|
||||||
|
// 如果数值的绝对值大于1000或小于0.1,使用科学计数法
|
||||||
|
if (Math.abs(value) > 1000 || (Math.abs(value) < 0.1 && value !== 0)) {
|
||||||
|
return value.toExponential(1);
|
||||||
|
}
|
||||||
|
// 否则使用普通数字,保留一位小数
|
||||||
|
return value.toFixed(1);
|
||||||
|
},
|
||||||
|
maxTicksLimit: 8 // 限制刻度数量
|
||||||
|
}
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
display: true,
|
||||||
|
ticks: {
|
||||||
|
callback: function(value) {
|
||||||
|
// 如果数值大于1000,使用科学计数法
|
||||||
|
if (value > 1000) {
|
||||||
|
return value.toExponential(1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: 'top'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: true,
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false,
|
||||||
|
callbacks: {
|
||||||
|
label: function(context) {
|
||||||
|
const value = context.raw;
|
||||||
|
// 始终使用普通数字格式,保留一位小数
|
||||||
|
return `${context.dataset.label}: ${value.toFixed(1)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 深度合并配置
|
||||||
|
const finalOptions = {
|
||||||
|
...baseOptions,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
...baseOptions.scales.y,
|
||||||
|
...(chartOptions.scales?.y || {}),
|
||||||
|
ticks: {
|
||||||
|
...baseOptions.scales.y.ticks,
|
||||||
|
...(chartOptions.scales?.y?.ticks || {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
...baseOptions.scales.x,
|
||||||
|
...(chartOptions.scales?.x || {}),
|
||||||
|
ticks: {
|
||||||
|
...baseOptions.scales.x.ticks,
|
||||||
|
...(chartOptions.scales?.x?.ticks || {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
...baseOptions.plugins,
|
||||||
|
...(chartOptions.plugins || {}),
|
||||||
|
tooltip: {
|
||||||
|
...baseOptions.plugins.tooltip,
|
||||||
|
...(chartOptions.plugins?.tooltip || {}),
|
||||||
|
callbacks: {
|
||||||
|
...baseOptions.plugins.tooltip.callbacks,
|
||||||
|
...(chartOptions.plugins?.tooltip?.callbacks || {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.chartInstance = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: chartData,
|
||||||
|
options: finalOptions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListeners() {
|
||||||
|
const header = this.shadowRoot.querySelector('.window-header');
|
||||||
|
const minimizeButton = this.shadowRoot.querySelector('.minimize-button');
|
||||||
|
const closeButton = this.shadowRoot.querySelector('.close-button');
|
||||||
|
const resizeHandle = this.shadowRoot.querySelector('.resize-handle');
|
||||||
|
|
||||||
|
header.addEventListener('mousedown', this.handleMouseDown);
|
||||||
|
minimizeButton.addEventListener('click', this.handleMinimize);
|
||||||
|
closeButton.addEventListener('click', this.handleClose);
|
||||||
|
resizeHandle.addEventListener('mousedown', this.handleResizeStart);
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', this.handleMouseMove);
|
||||||
|
document.addEventListener('mouseup', this.handleMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeEventListeners() {
|
||||||
|
document.removeEventListener('mousemove', this.handleMouseMove);
|
||||||
|
document.removeEventListener('mouseup', this.handleMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseDown(e) {
|
||||||
|
// 检查点击是否在标题栏或其子元素上
|
||||||
|
const header = this.shadowRoot.querySelector('.window-header');
|
||||||
|
if (header && (e.target === header || header.contains(e.target)) && !this.isMinimized) {
|
||||||
|
this.isDragging = true;
|
||||||
|
this.dragOffset = {
|
||||||
|
x: e.clientX - this.position.x,
|
||||||
|
y: e.clientY - this.position.y
|
||||||
|
};
|
||||||
|
// 点击标题栏时置顶
|
||||||
|
const window = this.shadowRoot.querySelector('.floating-window');
|
||||||
|
if (window) {
|
||||||
|
this.zIndex = FloatingChartWindow.zIndexCounter++;
|
||||||
|
window.style.zIndex = this.zIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseMove(e) {
|
||||||
|
if (this.isDragging) {
|
||||||
|
// 限制拖动范围在当前页面区域
|
||||||
|
const minX = 300; // 左侧工具栏宽度
|
||||||
|
const minY = 100; // 顶部标签页栏高度
|
||||||
|
|
||||||
|
this.position = {
|
||||||
|
x: Math.max(minX, e.clientX - this.dragOffset.x),
|
||||||
|
y: Math.max(minY, e.clientY - this.dragOffset.y)
|
||||||
|
};
|
||||||
|
this.updatePosition();
|
||||||
|
} else if (this.isResizing && !this.isMinimized) {
|
||||||
|
const deltaX = e.clientX - this.resizeStart.x;
|
||||||
|
const deltaY = e.clientY - this.resizeStart.y;
|
||||||
|
|
||||||
|
this.size = {
|
||||||
|
width: Math.max(300, this.size.width + deltaX),
|
||||||
|
height: Math.max(200, this.size.height + deltaY)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.resizeStart = {
|
||||||
|
x: e.clientX,
|
||||||
|
y: e.clientY
|
||||||
|
};
|
||||||
|
|
||||||
|
this.updateSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseUp() {
|
||||||
|
this.isDragging = false;
|
||||||
|
this.isResizing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResizeStart(e) {
|
||||||
|
this.isResizing = true;
|
||||||
|
this.resizeStart = {
|
||||||
|
x: e.clientX,
|
||||||
|
y: e.clientY
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResize(e) {
|
||||||
|
if (this.isResizing && !this.isMinimized) {
|
||||||
|
const deltaX = e.clientX - this.resizeStart.x;
|
||||||
|
const deltaY = e.clientY - this.resizeStart.y;
|
||||||
|
|
||||||
|
this.size = {
|
||||||
|
width: Math.max(300, this.size.width + deltaX),
|
||||||
|
height: Math.max(200, this.size.height + deltaY)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.resizeStart = {
|
||||||
|
x: e.clientX,
|
||||||
|
y: e.clientY
|
||||||
|
};
|
||||||
|
|
||||||
|
this.updateSize();
|
||||||
|
|
||||||
|
// 更新图表大小
|
||||||
|
if (this.chartInstance) {
|
||||||
|
this.chartInstance.resize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResizeEnd() {
|
||||||
|
this.isResizing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMinimize() {
|
||||||
|
this.isMinimized = !this.isMinimized;
|
||||||
|
const content = this.shadowRoot.querySelector('.window-content');
|
||||||
|
const minimizeButton = this.shadowRoot.querySelector('.minimize-button');
|
||||||
|
|
||||||
|
if (this.isMinimized) {
|
||||||
|
content.classList.add('minimized');
|
||||||
|
minimizeButton.textContent = '□';
|
||||||
|
} else {
|
||||||
|
content.classList.remove('minimized');
|
||||||
|
minimizeButton.textContent = '−';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClose() {
|
||||||
|
this.dispatchEvent(new CustomEvent('close'));
|
||||||
|
this.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePosition() {
|
||||||
|
const window = this.shadowRoot.querySelector('.floating-window');
|
||||||
|
window.style.left = `${this.position.x}px`;
|
||||||
|
window.style.top = `${this.position.y}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSize() {
|
||||||
|
const window = this.shadowRoot.querySelector('.floating-window');
|
||||||
|
window.style.width = `${this.size.width}px`;
|
||||||
|
window.style.height = `${this.size.height}px`;
|
||||||
|
|
||||||
|
// 强制图表重新渲染
|
||||||
|
if (this.chartInstance) {
|
||||||
|
this.chartInstance.resize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTitle(title) {
|
||||||
|
const titleElement = this.shadowRoot.querySelector('.window-title');
|
||||||
|
titleElement.textContent = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateChartData(data) {
|
||||||
|
if (this.chartInstance) {
|
||||||
|
// 更新数据
|
||||||
|
this.chartInstance.data = data;
|
||||||
|
|
||||||
|
// 强制更新配置
|
||||||
|
const options = this.chartInstance.options;
|
||||||
|
options.scales.y.ticks.callback = function(value) {
|
||||||
|
if (Math.abs(value) > 1000 || (Math.abs(value) < 0.1 && value !== 0)) {
|
||||||
|
return value.toExponential(1);
|
||||||
|
}
|
||||||
|
return value.toFixed(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
options.scales.x.ticks.callback = function(value) {
|
||||||
|
if (value > 1000) {
|
||||||
|
return value.toExponential(1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
options.plugins.tooltip.callbacks.label = function(context) {
|
||||||
|
const value = context.raw;
|
||||||
|
// 始终使用普通数字格式,保留一位小数
|
||||||
|
return `${context.dataset.label}: ${value.toFixed(1)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新图表
|
||||||
|
this.chartInstance.update('none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateChartOptions(options) {
|
||||||
|
if (this.chartInstance) {
|
||||||
|
Object.assign(this.chartInstance.options, options);
|
||||||
|
this.chartInstance.update('none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateZIndex(zIndex) {
|
||||||
|
const window = this.shadowRoot.querySelector('.floating-window');
|
||||||
|
if (window) {
|
||||||
|
window.style.zIndex = zIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('floating-chart-window', FloatingChartWindow);
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user