373fc0496c
统一简化多个页面里的CopyButton调用,删除不再需要的空回调参数 * feat: 添加clearCookies函数的单元测试并修复cookie域名处理逻辑 * feat: 增强 data URI 处理,支持带参数的前缀并更新相关测试 * fix: 修正 AGENTS.md 中 TypeScript 类型检查命令的描述 * feat: 添加 settings.local.json 文件以配置 Bash 权限 * perf: 预设背景色避免 Popup 弹窗白屏闪烁 * perf: 避免图标过早实例化,传递组件引用而非 JSX 节点 * feat: 添加 useLazyTranslation 和 preloadNamespaces 函数以支持动态加载 i18n 命名空间 * feat: 使用 useLazyTranslation 替换 useTranslation 以支持懒加载翻译 * feat: 添加 PageSkeleton 组件及其测试用例以支持页面加载骨架屏 * feat: 使用骨架屏替换加载状态指示器,优化用户体验 * feat: 优化 CopyButton 组件的复制功能,添加定时器管理复制状态 * feat: 调整 chunk 大小警告阈值以优化构建性能
130 lines
4.3 KiB
TypeScript
130 lines
4.3 KiB
TypeScript
import { Box, Container, MenuItem, Select, Stack, TextField } from '@mui/material';
|
|
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
|
import Button from '@/components/Button';
|
|
import PageHeader from '@/components/PageHeader';
|
|
import SwitchButtonGroup from '@/components/SwitchButtonGroup';
|
|
import { timestampPageStyles, ZONES } from '@/config/pageTheme';
|
|
import LiveClock from './LiveClock';
|
|
import ResultView from './ResultView';
|
|
import { useTimestampConverter } from './useTimestampConverter';
|
|
import { useLazyTranslation } from '@/utils/useLazyTranslation';
|
|
|
|
export default function Index() {
|
|
const { t } = useLazyTranslation('timestamp');
|
|
const {
|
|
mode,
|
|
tsInput,
|
|
dtInput,
|
|
unit,
|
|
zone,
|
|
result,
|
|
error,
|
|
setMode,
|
|
setInput,
|
|
setUnit,
|
|
setZone,
|
|
handleUseNow,
|
|
convert,
|
|
} = useTimestampConverter();
|
|
|
|
return (
|
|
<Box>
|
|
<Container maxWidth="lg" sx={{ p: 2 }}>
|
|
{/* Header */}
|
|
<PageHeader
|
|
title={t('timestamp:pageTitle')}
|
|
subtitle={t('timestamp:pageSubtitle')}
|
|
icon={<AccessTimeIcon />}
|
|
/>
|
|
|
|
{/* 参考信息:分栏上方全宽单行参考条 */}
|
|
<LiveClock unit={unit} onUseNow={handleUseNow} />
|
|
|
|
{/* 桌面端 md+ 左右分栏;移动端单栏堆叠 */}
|
|
<Box sx={timestampPageStyles.LAYOUT_GRID}>
|
|
{/* 左栏:转换工作台 */}
|
|
<Box sx={timestampPageStyles.CONVERSION_CARD}>
|
|
{/* 模式切换 */}
|
|
<SwitchButtonGroup
|
|
value={mode}
|
|
options={[
|
|
{ value: 'ts2dt', label: t('timestamp:tsToDate') },
|
|
{ value: 'dt2ts', label: t('timestamp:dateToTs') },
|
|
]}
|
|
onChange={(newMode) => setMode(newMode)}
|
|
size="small"
|
|
/>
|
|
|
|
{/* 输入区 */}
|
|
<Stack spacing={1.5}>
|
|
<TextField
|
|
placeholder={
|
|
mode === 'ts2dt' ? t('timestamp:placeholderTs') : t('timestamp:placeholderDate')
|
|
}
|
|
value={mode === 'ts2dt' ? tsInput : dtInput}
|
|
onChange={(e) => setInput(e.target.value)}
|
|
error={!!error}
|
|
helperText={error}
|
|
fullWidth
|
|
sx={timestampPageStyles.INPUT_STYLE}
|
|
/>
|
|
|
|
{/* 单位+时区紧凑横排 */}
|
|
<Stack
|
|
direction="row"
|
|
spacing={1.5}
|
|
useFlexGap
|
|
sx={{ width: '100%', flexWrap: 'wrap' }}
|
|
>
|
|
<SwitchButtonGroup
|
|
value={unit}
|
|
options={[
|
|
{ value: 'ms', label: t('timestamp:unitMs') },
|
|
{ value: 's', label: t('timestamp:unitS') },
|
|
]}
|
|
onChange={(v) => setUnit(v as 'ms' | 's')}
|
|
sx={{ width: 'auto', mb: 0, flexShrink: 0 }}
|
|
size="small"
|
|
/>
|
|
|
|
<Select
|
|
value={zone}
|
|
onChange={(e) => setZone(e.target.value as typeof zone)}
|
|
sx={{
|
|
...timestampPageStyles.INPUT_STYLE,
|
|
flex: 1,
|
|
minWidth: 0,
|
|
borderRadius: 4,
|
|
'& .MuiSelect-select': {
|
|
overflow: 'hidden',
|
|
textOverflow: 'ellipsis',
|
|
whiteSpace: 'nowrap',
|
|
},
|
|
}}
|
|
MenuProps={timestampPageStyles.SELECT_MENU_PROPS}
|
|
>
|
|
{ZONES.map((z) => (
|
|
<MenuItem key={z} value={z} sx={{ fontSize: '0.8rem', fontWeight: 600 }}>
|
|
{z}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
</Stack>
|
|
</Stack>
|
|
|
|
{/* 立即转换 */}
|
|
<Button variant="contained" onClick={convert} sx={timestampPageStyles.CONVERT_BUTTON}>
|
|
{t('timestamp:convertButton')}
|
|
</Button>
|
|
</Box>
|
|
|
|
{/* 右栏:结果展示卡片 */}
|
|
<Box sx={timestampPageStyles.RESULT_COLUMN_CARD}>
|
|
<ResultView result={result} mode={mode} unit={unit} zone={zone} showEmptyPlaceholder />
|
|
</Box>
|
|
</Box>
|
|
</Container>
|
|
</Box>
|
|
);
|
|
}
|