mirror of
https://mirror.skon.top/github.com/cft0808/edict
synced 2026-04-20 21:00:16 +08:00
fix(dashboard): normalize task modal timestamps to local time (#282)
新增共享 time.ts 时间工具,统一 TaskModal 中调度时间、流转日志、活动时间的本地时区渲染,消除 UTC/本地混显问题
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useState, useRef, useCallback } from 'react';
|
||||
import { useStore, getPipeStatus, deptColor, stateLabel, STATE_LABEL } from '../store';
|
||||
import { api } from '../api';
|
||||
import { formatDashboardDateTime, formatDashboardTime } from '../time';
|
||||
import type {
|
||||
Task,
|
||||
TaskActivityData,
|
||||
@@ -43,13 +44,7 @@ function fmtStalled(sec: number): string {
|
||||
}
|
||||
|
||||
function fmtActivityTime(ts: number | string | undefined): string {
|
||||
if (!ts) return '';
|
||||
if (typeof ts === 'number') {
|
||||
const d = new Date(ts);
|
||||
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`;
|
||||
}
|
||||
if (typeof ts === 'string' && ts.length >= 19) return ts.substring(11, 19);
|
||||
return String(ts).substring(0, 8);
|
||||
return formatDashboardTime(ts, { showSeconds: true });
|
||||
}
|
||||
|
||||
export default function TaskModal() {
|
||||
@@ -302,8 +297,8 @@ export default function TaskModal() {
|
||||
</div>
|
||||
{sched && (
|
||||
<div className="sched-line">
|
||||
{sched.lastProgressAt && <span>最近进展 {(sched.lastProgressAt || '').replace('T', ' ').substring(0, 19)}</span>}
|
||||
{sched.lastDispatchAt && <span>最近派发 {(sched.lastDispatchAt || '').replace('T', ' ').substring(0, 19)}</span>}
|
||||
{sched.lastProgressAt && <span>最近进展 {formatDashboardDateTime(sched.lastProgressAt)}</span>}
|
||||
{sched.lastDispatchAt && <span>最近派发 {formatDashboardDateTime(sched.lastDispatchAt)}</span>}
|
||||
<span>自动回滚 {sched.autoRollback === false ? '关闭' : '开启'}</span>
|
||||
{sched.lastDispatchAgent && <span>目标 {sched.lastDispatchAgent}</span>}
|
||||
</div>
|
||||
@@ -365,7 +360,7 @@ export default function TaskModal() {
|
||||
const col = deptColor(fl.from || '');
|
||||
return (
|
||||
<div className="fl-item" key={i}>
|
||||
<div className="fl-time">{fl.at ? fl.at.substring(11, 16) : ''}</div>
|
||||
<div className="fl-time">{formatDashboardTime(fl.at, { showSeconds: false })}</div>
|
||||
<div className="fl-dot" style={{ background: col }} />
|
||||
<div className="fl-content">
|
||||
<div className="fl-who">
|
||||
@@ -458,7 +453,7 @@ function LiveActivitySection({
|
||||
const agentParts: string[] = [];
|
||||
if (data.agentLabel) agentParts.push(data.agentLabel);
|
||||
if (data.relatedAgents && data.relatedAgents.length > 1) agentParts.push(`${data.relatedAgents.length}个 Agent`);
|
||||
if (data.lastActive) agentParts.push(`最后活跃: ${data.lastActive}`);
|
||||
if (data.lastActive) agentParts.push(`最后活跃: ${formatDashboardDateTime(data.lastActive)}`);
|
||||
|
||||
// Phase durations
|
||||
const phaseDurations = data.phaseDurations || [];
|
||||
|
||||
57
edict/frontend/src/time.ts
Normal file
57
edict/frontend/src/time.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
function pad2(value: number): string {
|
||||
return String(value).padStart(2, '0');
|
||||
}
|
||||
|
||||
export function parseDashboardTimestamp(value: number | string | undefined | null): Date | null {
|
||||
if (value === undefined || value === null || value === '') return null;
|
||||
|
||||
if (typeof value === 'number') {
|
||||
const ms = Math.abs(value) < 1e12 ? value * 1000 : value;
|
||||
const d = new Date(ms);
|
||||
return Number.isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
|
||||
const raw = String(value).trim();
|
||||
if (!raw) return null;
|
||||
|
||||
if (/^\d+(\.\d+)?$/.test(raw)) {
|
||||
return parseDashboardTimestamp(Number(raw));
|
||||
}
|
||||
|
||||
let normalized = raw;
|
||||
if (normalized.includes(' ') && !normalized.includes('T')) {
|
||||
normalized = normalized.replace(' ', 'T');
|
||||
}
|
||||
|
||||
const looksIso = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(normalized);
|
||||
const hasTimezone = /(?:Z|[+\-]\d{2}:\d{2})$/i.test(normalized);
|
||||
if (looksIso && !hasTimezone) {
|
||||
normalized += 'Z';
|
||||
}
|
||||
|
||||
const d = new Date(normalized);
|
||||
return Number.isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
|
||||
export function formatDashboardTime(
|
||||
value: number | string | undefined | null,
|
||||
{ showSeconds = true }: { showSeconds?: boolean } = {}
|
||||
): string {
|
||||
const d = parseDashboardTimestamp(value);
|
||||
if (!d) return '';
|
||||
const hh = pad2(d.getHours());
|
||||
const mm = pad2(d.getMinutes());
|
||||
if (!showSeconds) return `${hh}:${mm}`;
|
||||
return `${hh}:${mm}:${pad2(d.getSeconds())}`;
|
||||
}
|
||||
|
||||
export function formatDashboardDateTime(
|
||||
value: number | string | undefined | null,
|
||||
{ showSeconds = true }: { showSeconds?: boolean } = {}
|
||||
): string {
|
||||
const d = parseDashboardTimestamp(value);
|
||||
if (!d) return '';
|
||||
const date = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
||||
const time = formatDashboardTime(d.getTime(), { showSeconds });
|
||||
return `${date} ${time}`;
|
||||
}
|
||||
Reference in New Issue
Block a user