diff --git a/src/App.vue b/src/App.vue index f9a9af6..5ba686a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -91,9 +91,4 @@ body { } .app-main { flex: 1; display: flex; overflow: hidden; } .detail-area { flex: 1; overflow-y: auto; } -.empty { - text-align: center; - padding: 60px 20px; - color: #95a5a6; -} diff --git a/src/components/DropZone.vue b/src/components/DropZone.vue index a124a1d..b3031d8 100644 --- a/src/components/DropZone.vue +++ b/src/components/DropZone.vue @@ -39,7 +39,6 @@ function onPick(e) { @drop.prevent="onDrop" >
-

拖入 .ics 文件,或

-
@@ -118,21 +125,19 @@ function tooltip(ev) {
-
+
{{ headers[idx] }} {{ parseISODate(dateStr).day }}
-
-
MAX_HISTORY) state.history.shift() - state.redoStack = [] } function restore(snap) { @@ -44,7 +42,7 @@ function restore(snap) { } // ------------------------------------------------------------------ // -// 操作 +// 文件加载 // ------------------------------------------------------------------ // function loadFile(file) { return file.text().then((text) => { @@ -53,12 +51,14 @@ function loadFile(file) { state.events = parsed.events state.selectedUid = null state.history = [] - state.redoStack = [] state.fileName = file.name state.dirty = false }) } +// ------------------------------------------------------------------ // +// 事件操作 +// ------------------------------------------------------------------ // function selectEvent(uid) { state.selectedUid = uid } @@ -72,6 +72,10 @@ function organize() { return result.stats } +/** + * 更新指定事件的字段 (自动记录历史)。 + * 供 EventDetail 的自动应用 (watch) 调用。 + */ function updateEvent(uid, patch) { pushHistory() const ev = state.events.find((e) => e.uid === uid) @@ -127,16 +131,12 @@ function toggleOccurrence(uid, date) { function undo() { if (state.history.length === 0) return - state.redoStack.push(snapshot()) restore(state.history.pop()) } -function redo() { - if (state.redoStack.length === 0) return - state.history.push(snapshot()) - restore(state.redoStack.pop()) -} - +// ------------------------------------------------------------------ // +// 序列化 +// ------------------------------------------------------------------ // function serialize() { return serializeICS({ meta: state.meta, events: state.events }) } @@ -152,16 +152,18 @@ function setViewMode(mode) { state.viewMode = mode } -function prevMonth() { +function shiftMonth(delta) { const { year, month } = parseISODate(startOfMonth(state.previewDate)) - const d = new Date(year, month - 2, 1) // month-2 = 上个月 (0 基) + const d = new Date(year, month - 1 + delta, 1) state.previewDate = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-01` } +function prevMonth() { + shiftMonth(-1) +} + function nextMonth() { - const { year, month } = parseISODate(startOfMonth(state.previewDate)) - const d = new Date(year, month, 1) // month = 下个月 (0 基) - state.previewDate = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-01` + shiftMonth(1) } function prevWeek() { @@ -180,7 +182,6 @@ const selectedEvent = computed(() => ) const canUndo = computed(() => state.history.length > 0) -const canRedo = computed(() => state.redoStack.length > 0) const isLoaded = computed(() => state.events.length > 0) const stats = computed(() => { const nRecur = state.events.filter((e) => e.rrule !== null).length @@ -192,7 +193,6 @@ export function useCalendar() { state, selectedEvent, canUndo, - canRedo, isLoaded, stats, loadFile, @@ -204,7 +204,6 @@ export function useCalendar() { splitEvent, toggleOccurrence, undo, - redo, serialize, markSaved, setViewMode, diff --git a/src/lib/date.js b/src/lib/date.js index 4f55601..830def4 100644 --- a/src/lib/date.js +++ b/src/lib/date.js @@ -52,11 +52,6 @@ export function nowCompactTimestamp() { return new Date().toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z' } -/** 将 JS Date 的时间格式化为 'HH:MM'。 */ -export function toHHMM(date) { - return `${pad2(date.getHours())}:${pad2(date.getMinutes())}` -} - /** * 返回 isoDate 所在周的周一 (ISO 周, 周一为首日)。 * @param {string} isoDate - 'YYYY-MM-DD' diff --git a/src/lib/weekday.js b/src/lib/weekday.js index 2a33d68..ed89986 100644 --- a/src/lib/weekday.js +++ b/src/lib/weekday.js @@ -4,14 +4,7 @@ export const DOW = ['周日', '周一', '周二', '周三', '周四', '周五', // BYDAY 编码, 按 JS getDay() 索引: DOW_EN[getDay()] => 'SU'..'SA' export const DOW_EN = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'] -export const BYDAY_TO_INDEX = { SU: 0, MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6 } - /** 返回 JS Date 对应的 BYDAY 编码 ('SU'..'SA')。 */ export function bydayFromDate(date) { return DOW_EN[date.getDay()] } - -/** 返回 JS Date 对应的中文星期标签。 */ -export function dowLabel(date) { - return DOW[date.getDay()] -} diff --git a/test/weekday.test.js b/test/weekday.test.js index 62b3bca..02124fb 100644 --- a/test/weekday.test.js +++ b/test/weekday.test.js @@ -1,31 +1,21 @@ import { describe, it, expect } from 'vitest' -import { DOW, DOW_EN, BYDAY_TO_INDEX, bydayFromDate, dowLabel } from '../src/lib/weekday.js' +import { DOW, DOW_EN, bydayFromDate } from '../src/lib/weekday.js' describe('weekday', () => { - it('DOW has 7 entries starting Sunday', () => { + it('DOW 有 7 项, 首项为周日', () => { expect(DOW).toHaveLength(7) expect(DOW[0]).toBe('周日') expect(DOW[6]).toBe('周六') }) - it('DOW_EN indexed by JS getDay (0=Sunday)', () => { + it('DOW_EN 按 JS getDay 索引 (0=周日)', () => { expect(DOW_EN).toEqual(['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']) }) - it('BYDAY_TO_INDEX maps SU..SA to 0..6', () => { - expect(BYDAY_TO_INDEX.SU).toBe(0) - expect(BYDAY_TO_INDEX.SA).toBe(6) - }) - - it('bydayFromDate returns BYDAY code for a Date', () => { - // 2026-07-27 is a Monday -> getDay()=1 -> 'MO' + it('bydayFromDate 返回 Date 对应的 BYDAY 编码', () => { + // 2026-07-27 是周一 -> getDay()=1 -> 'MO' expect(bydayFromDate(new Date(2026, 6, 27))).toBe('MO') - // 2026-08-01 is a Saturday -> getDay()=6 -> 'SA' + // 2026-08-01 是周六 -> getDay()=6 -> 'SA' expect(bydayFromDate(new Date(2026, 7, 1))).toBe('SA') }) - - it('dowLabel returns Chinese label for a Date', () => { - expect(dowLabel(new Date(2026, 6, 27))).toBe('周一') - expect(dowLabel(new Date(2026, 6, 26))).toBe('周日') - }) })