diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index e5b2923..7db3ffb 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -307,7 +307,10 @@ function CalendarBoard({ todos: seedTodos, onTodoCreated }) { const [jumpOpen, setJumpOpen] = useState(false) const [jumpPos, setJumpPos] = useState(null) const [jumpMonth, setJumpMonth] = useState(() => new Date(today.getFullYear(), today.getMonth(), 1)) - // 年月拨盘:工具条月份文字与跳转浮层标题共用 + // 跳转浮层标题:直接输入年月(前 4 位年 + 后 1~2 位月,回车跳转) + const [jumpDraft, setJumpDraft] = useState(null) + const [jumpInvalid, setJumpInvalid] = useState(false) + // 年月拨盘:仅工具条月份文字使用 const [wheelOpen, setWheelOpen] = useState(false) const [wheelPos, setWheelPos] = useState(null) const [wheelPeriod, setWheelPeriod] = useState(() => ({ year: today.getFullYear(), month: today.getMonth() + 1 })) @@ -316,6 +319,7 @@ function CalendarBoard({ todos: seedTodos, onTodoCreated }) { const tipRef = useRef(null) const jumpRef = useRef(null) const jumpBtnRef = useRef(null) + const jumpInputRef = useRef(null) const wheelRef = useRef(null) const wheelAnchorRef = useRef(null) const anchorElRef = useRef(null) @@ -577,6 +581,42 @@ function CalendarBoard({ todos: seedTodos, onTodoCreated }) { const wheelGoToday = () => applyWheelPeriod(today.getFullYear(), today.getMonth() + 1) + /* 年月两种写法:「2026年9月」↔「202609」 */ + const compactPeriod = (date) => `${date.getFullYear()}${String(date.getMonth() + 1).padStart(2, '0')}` + + /* 进入编辑态:换成紧凑数字串并全选,方便直接改;已在编辑中就不打断输入 */ + const primeJumpInput = () => { + if (jumpDraft !== null) return + setJumpInvalid(false) + setJumpDraft(compactPeriod(jumpMonth)) + requestAnimationFrame(() => jumpInputRef.current?.select()) + } + + const changeJumpInput = (event) => { + setJumpDraft(event.target.value.replace(/\D/g, '').slice(0, 6)) + if (jumpInvalid) setJumpInvalid(false) + } + + /* 前 4 位是年,剩下 1~2 位是月;只写 4 位则沿用当前月 */ + const commitJumpInput = () => { + const digits = (jumpDraft ?? '').replace(/\D/g, '') + if (digits.length < 4) { setJumpInvalid(true); return false } + const year = Number(digits.slice(0, 4)) + const tail = digits.slice(4) + const monthNum = tail ? Number(tail) : jumpMonth.getMonth() + 1 + if (year < 1900 || year > 2999 || monthNum < 1 || monthNum > 12) { setJumpInvalid(true); return false } + setJumpMonth(new Date(year, monthNum - 1, 1)) + setJumpInvalid(false) + setJumpDraft(null) + jumpInputRef.current?.blur() + return true + } + + const cancelJumpInput = () => { + setJumpDraft(null) + setJumpInvalid(false) + } + // 点空白处收起跳转浮层(点按钮本身交给 toggleJump 处理) useEffect(() => { if (!jumpOpen) return undefined @@ -878,7 +918,26 @@ function CalendarBoard({ todos: seedTodos, onTodoCreated }) { >
- + { + if (event.key === 'Enter') { event.preventDefault(); commitJumpInput(); return } + if (event.key === 'Escape') { event.stopPropagation(); event.preventDefault(); cancelJumpInput(); event.currentTarget.blur() } + }} + onBlur={cancelJumpInput} + inputMode="numeric" + autoComplete="off" + spellCheck={false} + aria-label="输入年月快速跳转" + aria-invalid={jumpInvalid || undefined} + title="前 4 位是年、后 1~2 位是月,如 202609,回车跳转" + />
{WEEKDAYS.map(day => {day.slice(1)})}
diff --git a/frontend/src/styles/luxury.css b/frontend/src/styles/luxury.css index f9a950b..f537526 100644 --- a/frontend/src/styles/luxury.css +++ b/frontend/src/styles/luxury.css @@ -438,9 +438,12 @@ input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px .jump-day.is-selected { border-color: var(--primary); background: var(--primary); color: var(--surface-deep); font-weight: 800; } .jump-today { width: 100%; height: 27px; margin-top: 8px; border: 1px solid var(--border-light); border-radius: 7px; background: var(--surface-deep); color: var(--text-secondary); font-size: 11px; cursor: pointer; } .jump-today:hover { border-color: var(--primary); color: var(--primary-light); } -/* 跳转浮层标题 = 可点的年月(弹出拨盘) */ -.jump-head .jump-month-btn { display: inline-flex; align-items: center; justify-content: center; flex: 1; width: auto; min-width: 0; height: 24px; padding: 0 8px; border-radius: 7px; color: var(--text-primary); font-size: 12px; font-variant-numeric: tabular-nums; } -.jump-head .jump-month-btn:hover { border-color: var(--primary-border); background: var(--primary-bg); color: var(--primary-light); } +/* 跳转浮层标题 = 直接输入年月(前 4 位年 + 后 1~2 位月,回车跳转) */ +.jump-head .jump-month-input { display: block; flex: 1; width: auto; min-width: 0; min-height: 0; height: 26px; padding: 0 8px; border: 1px solid var(--border-light); border-radius: 7px; background: var(--surface-deep); box-shadow: none; color: var(--text-primary); font: 12px var(--font-body); font-variant-numeric: tabular-nums; text-align: center; outline: none; cursor: text; transition: border-color var(--transition-fast), background var(--transition-fast), color var(--transition-fast); } +.jump-head .jump-month-input:hover { border-color: var(--primary-border); background: var(--primary-bg); } +.jump-head .jump-month-input:focus { border-color: var(--primary); background: var(--primary-bg); color: var(--primary-light); font-weight: 700; letter-spacing: .3px; } +.jump-head .jump-month-input.is-invalid { border-color: var(--danger); background: var(--danger-bg); color: var(--danger); } +.jump-head .jump-month-input::selection { background: var(--primary); color: var(--surface-deep); } /* 年月拨盘(iOS 闹钟式滚轮) */ .calendar-wheel-pop { --wheel-item: 32px; padding: 12px 14px 10px; } .ymw-body { display: grid; gap: 9px; }