Files
taskq/x/monitor/ui/components/time-range-picker.js
hupeh 326f2a371c feat: 优化监控仪表盘 UI
- 添加 appbar 导航栏,支持 Chart/Queues 视图切换
- appbar 切换使用 history API,支持浏览器前进/后退
- 图表视图占满整个可视区域
- queue-modal 共享 appbar 样式
- 修复 queue tab count 字段名大小写问题
- tooltip 跟随鼠标显示在右下方,移除箭头
- 图表 canvas 鼠标样式改为准星
- pause/resume 队列后刷新列表
- example 添加 flag 配置参数
2025-12-10 00:53:30 +08:00

166 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { LitElement, html, css } from 'lit';
class TimeRangePicker extends LitElement {
static properties = {
duration: { type: String },
endTime: { type: Number },
isLiveMode: { type: Boolean }
};
static durations = ['5m', '15m', '30m', '1h', '3h', '6h', '12h', '1d', '3d', '7d'];
static styles = css`
:host {
display: flex;
gap: 10px;
align-items: center;
}
.time-control {
display: flex;
align-items: center;
background: #424242;
border: 1px solid #616161;
border-radius: 4px;
overflow: hidden;
}
button {
background: transparent;
border: none;
color: #9e9e9e;
padding: 6px 10px;
cursor: pointer;
font-size: 1em;
}
button:hover {
background: #515151;
color: #e0e0e0;
}
.value {
padding: 6px 12px;
color: #e0e0e0;
font-size: 0.85em;
min-width: 40px;
text-align: center;
}
.end-value {
cursor: pointer;
}
.end-value:hover {
background: #515151;
}
.reset-btn:hover {
color: #ef5350;
}
`;
constructor() {
super();
this.duration = '1h';
this.endTime = null;
this.isLiveMode = true;
}
get durationIndex() {
return TimeRangePicker.durations.indexOf(this.duration);
}
parseDuration(dur) {
const match = dur.match(/^(\d+)([mhd])$/);
if (!match) return 3600;
const value = parseInt(match[1]);
const unit = match[2];
switch (unit) {
case 'm': return value * 60;
case 'h': return value * 3600;
case 'd': return value * 86400;
default: return 3600;
}
}
adjustDuration(delta) {
const durations = TimeRangePicker.durations;
const newIndex = Math.max(0, Math.min(durations.length - 1, this.durationIndex + delta));
this.duration = durations[newIndex];
this.emitChange();
}
adjustEndTime(delta) {
const durationSecs = this.parseDuration(this.duration);
const step = durationSecs / 2;
let newEndTime = this.endTime;
if (newEndTime === null) {
newEndTime = Math.floor(Date.now() / 1000);
}
newEndTime += delta * step;
const now = Math.floor(Date.now() / 1000);
if (newEndTime >= now) {
this.resetToNow();
return;
}
this.endTime = newEndTime;
this.isLiveMode = false;
this.emitChange();
}
resetToNow() {
this.endTime = null;
this.isLiveMode = true;
this.emitChange();
}
emitChange() {
this.dispatchEvent(new CustomEvent('change', {
detail: {
duration: this.duration,
endTime: this.endTime,
isLiveMode: this.isLiveMode
}
}));
}
formatEndTime() {
if (this.endTime === null) {
return 'now';
}
const date = new Date(this.endTime * 1000);
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
}).replace(/\//g, '-');
}
render() {
return html`
<div class="time-control">
<button @click=${() => this.adjustDuration(-1)}></button>
<span class="value">${this.duration}</span>
<button @click=${() => this.adjustDuration(1)}>+</button>
</div>
<div class="time-control">
<button @click=${() => this.adjustEndTime(-1)}></button>
<span class="value end-value" @click=${this.resetToNow}>
${this.formatEndTime()}
</span>
<button @click=${() => this.adjustEndTime(1)}></button>
<button class="reset-btn" @click=${this.resetToNow} title="Reset to now">×</button>
</div>
`;
}
}
customElements.define('time-range-picker', TimeRangePicker);