Files
testing-tool/src/pages/Timestamp/useTimestampConverter.ts
T
雨霖铃 0906d1f425 feat(Timestamp): 重构时间戳转换功能,新增 ConverterForm 组件
- 在 Timestamp 页面中引入 ConverterForm 组件,简化输入和转换逻辑。
- 新增常量定义,包括模式选项、单位选项和输入占位符,提升代码可读性。
- 更新 useTimestampConverter 钩子,添加输入占位符支持。
- 引入 ToolCard 组件以统一样式和结构,优化页面布局。
2026-06-19 10:27:01 +08:00

112 lines
3.0 KiB
TypeScript

import { useMemo, useState } from 'react';
import dayjs from '@/utils/dayjs';
import type { UnitType, ZoneType, ModeType } from './constants';
import { DATE_FORMAT, INPUT_PLACEHOLDERS, msToUnit, dayjsFromTimestamp } from './constants';
import { useContextMenuData } from '@/utils/useContextMenuData';
export interface UseTimestampConverterReturn {
mode: ModeType;
input: string;
unit: UnitType;
zone: ZoneType;
result: string;
error: string;
inputPlaceholder: string;
setMode: (mode: ModeType) => void;
setInput: (value: string) => void;
setUnit: (unit: UnitType) => void;
setZone: (zone: ZoneType) => void;
handleUseNow: (now: number) => void;
}
const TIMESTAMP_REGEX = /^\d+$/;
const MS_TIMESTAMP_MIN_LENGTH = 13;
function isTimestampLike(input: string): boolean {
const trimmed = input.trim();
return TIMESTAMP_REGEX.test(trimmed) && trimmed.length >= 10;
}
export function useTimestampConverter(): UseTimestampConverterReturn {
const [mode, setMode] = useState<ModeType>('ts2dt');
const [unit, setUnit] = useState<UnitType>('ms');
const [zone, setZone] = useState<ZoneType>('Asia/Shanghai');
const [input, setInput] = useState(() => String(Date.now()));
const { result, error } = useMemo(() => {
const rawInput = input.trim();
if (!rawInput) return { result: '', error: '' };
if (mode === 'ts2dt') {
const num = Number(rawInput);
if (isNaN(num)) {
return { result: '', error: '请输入有效数字' };
}
const d = dayjsFromTimestamp(num, unit);
if (!d.isValid()) {
return { result: '', error: '无效时间戳' };
}
return { result: d.tz(zone).format(DATE_FORMAT), error: '' };
} else {
const d = dayjs.tz(rawInput, DATE_FORMAT, zone);
if (!d.isValid()) {
return { result: '', error: '无效的日期格式' };
}
const ms = d.valueOf();
return { result: String(msToUnit(ms, unit)), error: '' };
}
}, [input, mode, unit, zone]);
const handleContextMenuData = (payload: string) => {
const trimmed = payload.trim();
if (isTimestampLike(trimmed)) {
setMode('ts2dt');
setUnit(trimmed.length >= MS_TIMESTAMP_MIN_LENGTH ? 'ms' : 's');
setInput(trimmed);
} else {
const d = dayjs(trimmed);
if (d.isValid()) {
setMode('dt2ts');
setInput(d.format(DATE_FORMAT));
} else {
setMode('ts2dt');
setInput(trimmed);
}
}
};
useContextMenuData({ featureKey: 'timestamp', onData: handleContextMenuData });
const handleUseNow = (now: number) => {
if (mode === 'ts2dt') {
setInput(String(msToUnit(now, unit)));
} else {
setInput(dayjs(now).tz(zone).format(DATE_FORMAT));
}
};
const handleSetMode = (newMode: ModeType) => {
setMode(newMode);
if (result && !error) {
setInput(result);
}
};
return {
mode,
input,
unit,
zone,
result,
error,
inputPlaceholder: INPUT_PLACEHOLDERS[mode],
setMode: handleSetMode,
setInput,
setUnit,
setZone,
handleUseNow,
};
}