57ea4d9858
- **docs**: 完善组件注释、README 目录结构及 AGENTS.md 文档。 - **refactor**: - 提取通用 `PageHeader`、`Button`、`DashboardCard` 及 `ErrorBoundary` 组件。 - 重构消息通信机制,采用 `@webext-core/messaging` 实现类型安全。 - 将全局通知系统重构为 `SnackbarProvider` (后合并至 `GlobalSnackbar`)。 - 迁移样式系统至 MUI 主题,移除冗余 CSS。 - 优化路由配置,支持独立标签页模式及页面懒加载。 - 移除未使用文件、URL 工具及表单映射相关功能。 - **feat**: - 新增配置导出功能(JSON)及状态提示。 - 新增侧边栏状态变化通知机制。 - 新增文本统计及 JWT 解析工具。 - 优化二维码生成与解析逻辑,换用更轻量的 `qrious` 和 `qr-scanner`。 - 增强高亮器功能,支持闪烁效果及 Shadow DOM 穿透。 - **style**: 优化仪表盘响应式网格布局及 UI 细节。 - **fix**: 修复 `useStorageState` 依赖缺失及路由初始化性能问题。 - **test**: 更新单元测试以覆盖新增的工具函数及功能特性。
114 lines
3.0 KiB
TypeScript
114 lines
3.0 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
import dayjs from '@/utils/dayjs';
|
|
import { DATE_FORMAT } from '@/config/pageTheme';
|
|
import type { UnitType, ZoneType } from '@/config/pageTheme';
|
|
|
|
export interface UseTimestampConverterReturn {
|
|
// State
|
|
mode: 'ts2dt' | 'dt2ts';
|
|
tsInput: string;
|
|
dtInput: string;
|
|
unit: UnitType;
|
|
zone: ZoneType;
|
|
result: string;
|
|
error: string;
|
|
|
|
// Actions
|
|
setMode: (mode: 'ts2dt' | 'dt2ts') => void;
|
|
setTsInput: (value: string) => void;
|
|
setDtInput: (value: string) => void;
|
|
setUnit: (unit: UnitType) => void;
|
|
setZone: (zone: ZoneType) => void;
|
|
handleUseNow: (now: number) => void;
|
|
convert: () => void;
|
|
}
|
|
|
|
export function useTimestampConverter(): UseTimestampConverterReturn {
|
|
const [mode, setMode] = useState<'ts2dt' | 'dt2ts'>('ts2dt');
|
|
const [tsInput, setTsInput] = useState(() => String(Date.now()));
|
|
const [dtInput, setDtInput] = useState(() => dayjs().format(DATE_FORMAT));
|
|
const [unit, setUnit] = useState<UnitType>('ms');
|
|
const [zone, setZone] = useState<ZoneType>('Asia/Shanghai');
|
|
const [result, setResult] = useState('');
|
|
const [error, setError] = useState('');
|
|
|
|
const convert = useCallback(() => {
|
|
if (mode === 'ts2dt') {
|
|
const rawInput = tsInput.trim();
|
|
if (!rawInput) return;
|
|
const num = Number(rawInput);
|
|
if (isNaN(num)) {
|
|
setError('无效数字');
|
|
return;
|
|
}
|
|
const d = unit === 'ms' ? dayjs(num) : dayjs.unix(num);
|
|
if (!d.isValid()) {
|
|
setError('无效时间戳');
|
|
return;
|
|
}
|
|
setError('');
|
|
setResult(d.tz(zone).format(DATE_FORMAT));
|
|
} else {
|
|
const rawInput = dtInput.trim();
|
|
if (!rawInput) return;
|
|
const d = dayjs.tz(rawInput, DATE_FORMAT, zone);
|
|
if (!d.isValid()) {
|
|
setError('格式错误');
|
|
return;
|
|
}
|
|
setError('');
|
|
const ms = d.valueOf();
|
|
setResult(unit === 'ms' ? String(ms) : String(Math.floor(ms / 1000)));
|
|
}
|
|
}, [mode, tsInput, dtInput, unit, zone]);
|
|
|
|
useEffect(() => {
|
|
const timer = setTimeout(convert, 400);
|
|
return () => clearTimeout(timer);
|
|
}, [convert]);
|
|
|
|
const handleUseNow = useCallback(
|
|
(now: number) => {
|
|
if (mode === 'ts2dt') {
|
|
setTsInput(String(unit === 'ms' ? now : Math.floor(now / 1000)));
|
|
} else {
|
|
setDtInput(dayjs(now).tz(zone).format(DATE_FORMAT));
|
|
}
|
|
},
|
|
[mode, unit, zone],
|
|
);
|
|
|
|
const handleSetMode = useCallback((newMode: 'ts2dt' | 'dt2ts') => {
|
|
setMode(newMode);
|
|
setError('');
|
|
setResult('');
|
|
}, []);
|
|
|
|
const handleSetTsInput = useCallback((value: string) => {
|
|
setTsInput(value);
|
|
setError('');
|
|
}, []);
|
|
|
|
const handleSetDtInput = useCallback((value: string) => {
|
|
setDtInput(value);
|
|
setError('');
|
|
}, []);
|
|
|
|
return {
|
|
mode,
|
|
tsInput,
|
|
dtInput,
|
|
unit,
|
|
zone,
|
|
result,
|
|
error,
|
|
setMode: handleSetMode,
|
|
setTsInput: handleSetTsInput,
|
|
setDtInput: handleSetDtInput,
|
|
setUnit,
|
|
setZone,
|
|
handleUseNow,
|
|
convert,
|
|
};
|
|
}
|