refactor: 重构 Timestamp 页面使用响应式 Hook 统一状态并全面迁移至 shadcn 样式

This commit is contained in:
雨霖铃
2026-05-22 21:03:48 +08:00
parent 94158f016b
commit 02aef0c7b1
4 changed files with 252 additions and 184 deletions
+86 -54
View File
@@ -6,13 +6,25 @@ import LiveClock from './LiveClock';
import ResultView from './ResultView';
import { useTimestampConverter } from './useTimestampConverter';
import { useLazyTranslation } from '@/utils/useLazyTranslation';
import { cn } from '@/lib/utils';
// 1. 引入标准的 shadcn/ui 原子表单组件(代替原生的原生 Input 和 Select
import { Input } from '@/components/ui/input';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
export default function Index() {
const { t } = useLazyTranslation('timestamp');
// 2. 完美对接全新重构后的统一单源响应式 Hook
const {
mode,
tsInput,
dtInput,
input,
unit,
zone,
result,
@@ -22,12 +34,11 @@ export default function Index() {
setUnit,
setZone,
handleUseNow,
convert,
} = useTimestampConverter();
return (
<div className="min-h-screen">
<div className="max-w-7xl mx-auto p-4">
<div className="min-h-screen bg-background text-foreground antialiased selection:bg-primary/20">
<div className="max-w-7xl mx-auto p-4 sm:p-6 space-y-4">
{/* Header */}
<PageHeader
title={t('timestamp:pageTitle')}
@@ -35,41 +46,55 @@ export default function Index() {
icon={<Clock />}
/>
{/* 参考信息:分栏上方全宽单行参考条 */}
{/* 动态参考信息时钟条 */}
<LiveClock unit={unit} onUseNow={handleUseNow} />
{/* 桌面端 md+ 左右分栏;移动端单栏堆叠 */}
{/* 核心工作台网格:桌面端 md+ 左右等宽分栏;移动端单栏堆叠 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 items-stretch">
{/* 左栏:转换工作台 */}
<div className="p-5 rounded-xl bg-background border border-border shadow-sm">
{/* 模式切换 */}
<SwitchButtonGroup
value={mode}
options={[
{ value: 'ts2dt', label: t('timestamp:tsToDate') },
{ value: 'dt2ts', label: t('timestamp:dateToTs') },
]}
onChange={(newMode) => setMode(newMode)}
size="small"
/>
{/* 输入区 */}
<div className="flex flex-col gap-3">
<input
type="text"
placeholder={
mode === 'ts2dt' ? t('timestamp:placeholderTs') : t('timestamp:placeholderDate')
}
value={mode === 'ts2dt' ? tsInput : dtInput}
onChange={(e) => setInput(e.target.value)}
className={`w-full px-4 py-2.5 rounded-lg border ${
error ? 'border-red-500' : 'border-input'
} bg-background text-sm font-mono font-semibold focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent transition-all`}
<div className="p-5 rounded-xl border border-border bg-card text-card-foreground shadow-sm flex flex-col justify-between">
<div className="flex flex-col gap-4">
{/* 模式选择切换组 */}
<SwitchButtonGroup
value={mode}
options={[
{ value: 'ts2dt', label: t('timestamp:tsToDate') },
{ value: 'dt2ts', label: t('timestamp:dateToTs') },
]}
onChange={(newMode) => setMode(newMode as 'ts2dt' | 'dt2ts')}
size="small"
/>
{error && <p className="text-red-500 text-xs mt-1">{error}</p>}
{/* 单位+时区紧凑横排 */}
<div className="flex flex-wrap gap-3 w-full">
{/* 输入交互区 */}
<div className="flex flex-col gap-1.5">
{/*
3. 替换为标准的 shadcn <Input /> 组件:
享受原生高水准的 focus-visible 环形动画响应。
*/}
<Input
type="text"
placeholder={
mode === 'ts2dt' ? t('timestamp:placeholderTs') : t('timestamp:placeholderDate')
}
value={input}
onChange={(e: { target: { value: string } }) => setInput(e.target.value)}
className={cn(
'font-mono font-semibold h-10 shadow-sm placeholder:text-muted-foreground/60 focus:bg-background transition-all',
error && 'border-destructive focus-visible:ring-destructive',
)}
/>
{/* 错误自愈提示 */}
{error && (
<p className="text-destructive text-xs font-medium px-0.5 animate-in fade-in slide-in-from-top-1 duration-150">
{error}
</p>
)}
</div>
{/* 核心配置群:单位切换 + 时区选择紧凑横排 */}
<div className="flex flex-col sm:flex-row items-stretch gap-3 w-full">
{/* 时间精度单位选择 */}
<SwitchButtonGroup
value={unit}
options={[
@@ -78,33 +103,40 @@ export default function Index() {
]}
onChange={(v) => setUnit(v as 'ms' | 's')}
size="small"
className="sm:w-auto shrink-0" // 窄屏下全宽,宽屏下自适应收缩
/>
<select
value={zone}
onChange={(e) => setZone(e.target.value as typeof zone)}
className="flex-1 min-w-0 px-4 py-2.5 rounded-lg border border-input bg-background text-sm font-mono font-semibold focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent transition-all"
>
{ZONES.map((z) => (
<option key={z} value={z} className="text-sm font-semibold">
{z}
</option>
))}
</select>
{/*
4. 降维打击:将原生 <select> 强行超进化为标准的 shadcn <Select>
全操作系统的样式绝对一致,完美融合暗黑模式,悬浮弹窗自带微距磨砂玻璃阻尼动效。
*/}
<Select value={zone} onValueChange={(v: string) => setZone(v as typeof zone)}>
<SelectTrigger className="flex-1 font-mono font-semibold h-9 shadow-sm bg-background">
<SelectValue placeholder="选择时区" />
</SelectTrigger>
<SelectContent className="max-h-64 font-mono">
{ZONES.map((z) => (
<SelectItem
key={z}
value={z}
className="text-xs font-semibold focus:bg-accent cursor-pointer"
>
{z}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
{/* 立即转换 */}
<button
onClick={convert}
className="block mx-auto mt-4 mb-1 max-w-[240px] w-full py-2.5 text-sm font-semibold rounded-lg bg-primary text-primary-foreground shadow-md hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 transition-all"
>
{t('timestamp:convertButton')}
</button>
{/*
5. 彻底移除了原先丑陋的手动 convert 按钮!
在响应式 Hook 的加持下,此处留白或由排版自然撑开,界面视觉极其干净。
*/}
</div>
{/* 右栏:结果展示卡片 */}
<div className="p-5 rounded-xl bg-background border border-border shadow-sm h-full flex flex-col">
{/* 右栏:结果实时流展示卡片 */}
<div className="p-5 rounded-xl border border-border bg-card text-card-foreground shadow-sm h-full flex flex-col">
<ResultView result={result} mode={mode} unit={unit} zone={zone} showEmptyPlaceholder />
</div>
</div>