0906d1f425
- 在 Timestamp 页面中引入 ConverterForm 组件,简化输入和转换逻辑。 - 新增常量定义,包括模式选项、单位选项和输入占位符,提升代码可读性。 - 更新 useTimestampConverter 钩子,添加输入占位符支持。 - 引入 ToolCard 组件以统一样式和结构,优化页面布局。
112 lines
3.0 KiB
TypeScript
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,
|
|
};
|
|
}
|