设置页面添加12/24小时制开关,与时钟联动并保存到本地存储
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
651a2d8dc2
commit
3be055608e
2
.gitignore
vendored
2
.gitignore
vendored
@ -22,3 +22,5 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
dist.tar.gz
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
source ~/.nvm/nvm.sh
|
||||
nvm use
|
||||
|
||||
npm run build
|
||||
tar zcvf dist.tar.gz dist
|
||||
|
||||
130
src/App.css
130
src/App.css
@ -13,6 +13,7 @@
|
||||
width: 100%;
|
||||
padding-top: 40px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Style selector */
|
||||
@ -183,3 +184,132 @@
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Settings button */
|
||||
.settings-button {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 8px;
|
||||
font-size: 20px;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.settings-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
/* Settings overlay */
|
||||
.settings-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
/* Settings dialog */
|
||||
.settings-dialog {
|
||||
background-color: var(--bg);
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
width: 90%;
|
||||
max-width: 400px;
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.settings-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.settings-header h3 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
color: var(--text-h);
|
||||
}
|
||||
|
||||
.settings-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
color: var(--text);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.settings-close:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.settings-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.settings-label {
|
||||
font-size: 16px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.settings-toggle {
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
padding: 2px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.settings-toggle.active {
|
||||
background-color: var(--accent);
|
||||
}
|
||||
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.settings-toggle.active .toggle-slider {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
118
src/App.tsx
118
src/App.tsx
@ -31,7 +31,7 @@ function getTimezoneInfo(): string {
|
||||
return `${timeZone} ${offset}`
|
||||
}
|
||||
|
||||
function DigitalClock({ time, size, period, is24Hour, onToggleFormat, dateInfo, timeZone }: { time: string; size: number; period?: string; is24Hour: boolean; onToggleFormat: () => void; dateInfo: DateInfo; timeZone: string }) {
|
||||
function DigitalClock({ time, size, period, is24Hour, onToggleFormat, dateInfo, timeZone, showDate, showTimezone }: { time: string; size: number; period?: string; is24Hour: boolean; onToggleFormat: () => void; dateInfo: DateInfo; timeZone: string; showDate: boolean; showTimezone: boolean }) {
|
||||
const fontSize = Math.floor(size * 0.95)
|
||||
const periodSize = Math.floor(size * 0.20)
|
||||
const dateSize = Math.floor(size * 0.20)
|
||||
@ -59,12 +59,14 @@ function DigitalClock({ time, size, period, is24Hour, onToggleFormat, dateInfo,
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
className="digital-clock-date"
|
||||
style={{ fontSize: `${dateSize}px` }}
|
||||
>
|
||||
{dateInfo.dateString} {dateInfo.weekday}
|
||||
</span>
|
||||
{showDate && (
|
||||
<span
|
||||
className="digital-clock-date"
|
||||
style={{ fontSize: `${dateSize}px` }}
|
||||
>
|
||||
{dateInfo.dateString} {dateInfo.weekday}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="digital-clock"
|
||||
@ -72,14 +74,16 @@ function DigitalClock({ time, size, period, is24Hour, onToggleFormat, dateInfo,
|
||||
>
|
||||
{time}
|
||||
</div>
|
||||
<div className="digital-clock-footer">
|
||||
<span
|
||||
className="digital-clock-timezone"
|
||||
style={{ fontSize: `${timeZoneSize}px` }}
|
||||
>
|
||||
{timeZone}
|
||||
</span>
|
||||
</div>
|
||||
{showTimezone && (
|
||||
<div className="digital-clock-footer">
|
||||
<span
|
||||
className="digital-clock-timezone"
|
||||
style={{ fontSize: `${timeZoneSize}px` }}
|
||||
>
|
||||
{timeZone}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@ -104,6 +108,17 @@ function App() {
|
||||
const saved = localStorage.getItem('clock-rollDirection')
|
||||
return saved === 'up' ? 'up' : 'down'
|
||||
})
|
||||
const [showSettings, setShowSettings] = useState(false)
|
||||
const [showDate, setShowDate] = useState(() => {
|
||||
// 从 localStorage 读取,默认显示日期 (true)
|
||||
const saved = localStorage.getItem('clock-showDate')
|
||||
return saved === null ? true : saved === 'true'
|
||||
})
|
||||
const [showTimezone, setShowTimezone] = useState(() => {
|
||||
// 从 localStorage 读取,默认显示时区 (true)
|
||||
const saved = localStorage.getItem('clock-showTimezone')
|
||||
return saved === null ? true : saved === 'true'
|
||||
})
|
||||
|
||||
// 切换样式时保存到 localStorage
|
||||
const handleSetClockStyle = (style: ClockStyle) => {
|
||||
@ -125,6 +140,20 @@ function App() {
|
||||
localStorage.setItem('clock-rollDirection', newValue)
|
||||
}
|
||||
|
||||
// 切换显示日期时保存到 localStorage
|
||||
const toggleShowDate = () => {
|
||||
const newValue = !showDate
|
||||
setShowDate(newValue)
|
||||
localStorage.setItem('clock-showDate', String(newValue))
|
||||
}
|
||||
|
||||
// 切换显示时区时保存到 localStorage
|
||||
const toggleShowTimezone = () => {
|
||||
const newValue = !showTimezone
|
||||
setShowTimezone(newValue)
|
||||
localStorage.setItem('clock-showTimezone', String(newValue))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setTime(new Date())
|
||||
@ -174,9 +203,9 @@ function App() {
|
||||
const renderClock = () => {
|
||||
switch (clockStyle) {
|
||||
case 'flip':
|
||||
return <FlipClock time={timeString} size={clockSize} dateInfo={dateInfo} period={period} is24Hour={is24Hour} onToggleFormat={toggleFormat} timeZone={timeZone} />
|
||||
return <FlipClock time={timeString} size={clockSize} dateInfo={dateInfo} period={period} is24Hour={is24Hour} onToggleFormat={toggleFormat} timeZone={timeZone} showDate={showDate} showTimezone={showTimezone} />
|
||||
case 'roll':
|
||||
return <RollClock time={timeString} size={clockSize} dateInfo={dateInfo} period={period} is24Hour={is24Hour} onToggleFormat={toggleFormat} direction={rollDirection} onToggleDirection={toggleRollDirection} timeZone={timeZone} />
|
||||
return <RollClock time={timeString} size={clockSize} dateInfo={dateInfo} period={period} is24Hour={is24Hour} onToggleFormat={toggleFormat} direction={rollDirection} onToggleDirection={toggleRollDirection} timeZone={timeZone} showDate={showDate} showTimezone={showTimezone} />
|
||||
case 'digital':
|
||||
default:
|
||||
return (
|
||||
@ -188,6 +217,8 @@ function App() {
|
||||
onToggleFormat={toggleFormat}
|
||||
dateInfo={dateInfo}
|
||||
timeZone={timeZone}
|
||||
showDate={showDate}
|
||||
showTimezone={showTimezone}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -195,6 +226,59 @@ function App() {
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<button
|
||||
className="settings-button"
|
||||
onClick={() => setShowSettings(true)}
|
||||
aria-label="设置"
|
||||
>
|
||||
⚙️
|
||||
</button>
|
||||
|
||||
{showSettings && (
|
||||
<div className="settings-overlay" onClick={() => setShowSettings(false)}>
|
||||
<div className="settings-dialog" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="settings-header">
|
||||
<h3>设置</h3>
|
||||
<button
|
||||
className="settings-close"
|
||||
onClick={() => setShowSettings(false)}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="settings-content">
|
||||
<div className="settings-row">
|
||||
<span className="settings-label">12小时制</span>
|
||||
<button
|
||||
className={`settings-toggle ${!is24Hour ? 'active' : ''}`}
|
||||
onClick={toggleFormat}
|
||||
>
|
||||
<span className="toggle-slider" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="settings-row">
|
||||
<span className="settings-label">显示日期</span>
|
||||
<button
|
||||
className={`settings-toggle ${showDate ? 'active' : ''}`}
|
||||
onClick={toggleShowDate}
|
||||
>
|
||||
<span className="toggle-slider" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="settings-row">
|
||||
<span className="settings-label">显示时区</span>
|
||||
<button
|
||||
className={`settings-toggle ${showTimezone ? 'active' : ''}`}
|
||||
onClick={toggleShowTimezone}
|
||||
>
|
||||
<span className="toggle-slider" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="style-selector">
|
||||
<button
|
||||
className={`style-button ${clockStyle === 'digital' ? 'active' : ''}`}
|
||||
|
||||
@ -14,9 +14,11 @@ interface FlipClockProps {
|
||||
is24Hour: boolean;
|
||||
onToggleFormat: () => void;
|
||||
timeZone: string;
|
||||
showDate: boolean;
|
||||
showTimezone: boolean;
|
||||
}
|
||||
|
||||
export function FlipClock({ time, size, dateInfo, period, is24Hour, onToggleFormat, timeZone }: FlipClockProps) {
|
||||
export function FlipClock({ time, size, dateInfo, period, is24Hour, onToggleFormat, timeZone, showDate, showTimezone }: FlipClockProps) {
|
||||
// Parse time - it's already in 12h format when period is provided
|
||||
const [hoursStr, minutes, seconds] = time.split(':');
|
||||
const displayHours = hoursStr;
|
||||
@ -47,12 +49,14 @@ export function FlipClock({ time, size, dateInfo, period, is24Hour, onToggleForm
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
className="flip-clock-date"
|
||||
style={{ fontSize: `${dateSize}px` }}
|
||||
>
|
||||
{dateInfo.dateString} {dateInfo.weekday}
|
||||
</span>
|
||||
{showDate && (
|
||||
<span
|
||||
className="flip-clock-date"
|
||||
style={{ fontSize: `${dateSize}px` }}
|
||||
>
|
||||
{dateInfo.dateString} {dateInfo.weekday}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flip-clock-container">
|
||||
{/* Hours */}
|
||||
@ -93,14 +97,16 @@ export function FlipClock({ time, size, dateInfo, period, is24Hour, onToggleForm
|
||||
<FlipDigit value={parseInt(seconds[1], 10)} size={size} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flip-clock-footer">
|
||||
<span
|
||||
className="flip-clock-timezone"
|
||||
style={{ fontSize: `${timeZoneSize}px` }}
|
||||
>
|
||||
{timeZone}
|
||||
</span>
|
||||
</div>
|
||||
{showTimezone && (
|
||||
<div className="flip-clock-footer">
|
||||
<span
|
||||
className="flip-clock-timezone"
|
||||
style={{ fontSize: `${timeZoneSize}px` }}
|
||||
>
|
||||
{timeZone}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -16,9 +16,11 @@ interface RollClockProps {
|
||||
direction: 'down' | 'up';
|
||||
onToggleDirection: () => void;
|
||||
timeZone: string;
|
||||
showDate: boolean;
|
||||
showTimezone: boolean;
|
||||
}
|
||||
|
||||
export function RollClock({ time, size, dateInfo, period, is24Hour, onToggleFormat, direction, onToggleDirection, timeZone }: RollClockProps) {
|
||||
export function RollClock({ time, size, dateInfo, period, is24Hour, onToggleFormat, direction, onToggleDirection, timeZone, showDate, showTimezone }: RollClockProps) {
|
||||
// Parse time - it's already in 12h format when period is provided
|
||||
const [hoursStr, minutes, seconds] = time.split(':');
|
||||
const displayHours = hoursStr;
|
||||
@ -51,12 +53,14 @@ export function RollClock({ time, size, dateInfo, period, is24Hour, onToggleForm
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
className="roll-clock-date"
|
||||
style={{ fontSize: `${dateSize}px` }}
|
||||
>
|
||||
{dateInfo.dateString} {dateInfo.weekday}
|
||||
</span>
|
||||
{showDate && (
|
||||
<span
|
||||
className="roll-clock-date"
|
||||
style={{ fontSize: `${dateSize}px` }}
|
||||
>
|
||||
{dateInfo.dateString} {dateInfo.weekday}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="roll-clock-container">
|
||||
{/* Hours */}
|
||||
@ -97,14 +101,16 @@ export function RollClock({ time, size, dateInfo, period, is24Hour, onToggleForm
|
||||
<RollDigit value={parseInt(seconds[1], 10)} size={size} direction={direction} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="roll-clock-footer">
|
||||
<span
|
||||
className="roll-clock-timezone"
|
||||
style={{ fontSize: `${timeZoneSize}px` }}
|
||||
>
|
||||
{timeZone}
|
||||
</span>
|
||||
</div>
|
||||
{showTimezone && (
|
||||
<div className="roll-clock-footer">
|
||||
<span
|
||||
className="roll-clock-timezone"
|
||||
style={{ fontSize: `${timeZoneSize}px` }}
|
||||
>
|
||||
{timeZone}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Direction toggle button */}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user