feat: 新增视图切换状态管理及颜色/日期工具
- date.js: 新增 startOfWeek, startOfMonth, hhmmToMinutes 辅助函数 - colors.js (新增): 基于 uid 哈希的事件颜色分配, 12 色调色板 - useCalendar.js: 新增 viewMode (edit/month/week) 和 previewDate 状态, 以及 setViewMode/prevMonth/nextMonth/prevWeek/nextWeek 操作
This commit is contained in:
@@ -3,6 +3,7 @@ import { parseICS, serializeICS } from '../lib/ical-io.js'
|
||||
import { organize as organizeEvents } from '../lib/organize.js'
|
||||
import { createEvent, cloneEvent } from '../lib/event-factory.js'
|
||||
import { splitRecurringEvent } from '../lib/split.js'
|
||||
import { todayISO, addDays, startOfMonth, startOfWeek, parseISODate, pad2 } from '../lib/date.js'
|
||||
|
||||
const MAX_HISTORY = 50
|
||||
|
||||
@@ -15,6 +16,8 @@ const state = reactive({
|
||||
redoStack: [],
|
||||
fileName: null,
|
||||
dirty: false,
|
||||
viewMode: 'edit', // 'edit' | 'month' | 'week'
|
||||
previewDate: todayISO(), // 预览锚定日期 (月预览取当月, 周预览取当周)
|
||||
})
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
@@ -142,6 +145,33 @@ function markSaved() {
|
||||
state.dirty = false
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// 视图切换 / 预览导航
|
||||
// ------------------------------------------------------------------ //
|
||||
function setViewMode(mode) {
|
||||
state.viewMode = mode
|
||||
}
|
||||
|
||||
function prevMonth() {
|
||||
const { year, month } = parseISODate(startOfMonth(state.previewDate))
|
||||
const d = new Date(year, month - 2, 1) // month-2 = 上个月 (0 基)
|
||||
state.previewDate = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-01`
|
||||
}
|
||||
|
||||
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`
|
||||
}
|
||||
|
||||
function prevWeek() {
|
||||
state.previewDate = addDays(state.previewDate, -7)
|
||||
}
|
||||
|
||||
function nextWeek() {
|
||||
state.previewDate = addDays(state.previewDate, 7)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// 计算属性
|
||||
// ------------------------------------------------------------------ //
|
||||
@@ -177,5 +207,10 @@ export function useCalendar() {
|
||||
redo,
|
||||
serialize,
|
||||
markSaved,
|
||||
setViewMode,
|
||||
prevMonth,
|
||||
nextMonth,
|
||||
prevWeek,
|
||||
nextWeek,
|
||||
}
|
||||
}
|
||||
|
||||
44
src/lib/colors.js
Normal file
44
src/lib/colors.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 事件颜色分配工具。
|
||||
*
|
||||
* 基于事件 uid 的哈希值从预定义调色板中选取颜色, 确保相同事件始终
|
||||
* 同色, 不同事件大概率不同色。预定义调色板保证颜色区分度高且可读。
|
||||
*/
|
||||
|
||||
// 12 种区分度高的颜色, 每种包含背景色、边框色、文字色
|
||||
const PALETTE = [
|
||||
{ bg: '#e3f2fd', border: '#1976d2', text: '#0d47a1' }, // 蓝
|
||||
{ bg: '#f3e5f5', border: '#7b1fa2', text: '#4a148c' }, // 紫
|
||||
{ bg: '#e8f5e9', border: '#388e3c', text: '#1b5e20' }, // 绿
|
||||
{ bg: '#fff3e0', border: '#e65100', text: '#bf360c' }, // 橙
|
||||
{ bg: '#fce4ec', border: '#c2185b', text: '#880e4f' }, // 粉
|
||||
{ bg: '#e0f7fa', border: '#00838f', text: '#006064' }, // 青
|
||||
{ bg: '#f1f8e9', border: '#827717', text: '#33691e' }, // 黄绿
|
||||
{ bg: '#ede7f6', border: '#512da8', text: '#311b92' }, // 靛
|
||||
{ bg: '#fbe9e7', border: '#d84315', text: '#bf360c' }, // 红橙
|
||||
{ bg: '#e0f2f1', border: '#00695c', text: '#004d40' }, // 蓝绿
|
||||
{ bg: '#fff8e1', border: '#f9a825', text: '#f57f17' }, // 金
|
||||
{ bg: '#f9fbe7', border: '#afb42b', text: '#827717' }, // 橄榄
|
||||
]
|
||||
|
||||
/**
|
||||
* 对字符串进行简单哈希, 返回非负整数。
|
||||
* @param {string} str
|
||||
* @returns {number}
|
||||
*/
|
||||
function hashStr(str) {
|
||||
let h = 0
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
h = (h * 31 + str.charCodeAt(i)) | 0
|
||||
}
|
||||
return Math.abs(h)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 uid 返回调色板中的颜色对象。
|
||||
* @param {string} uid
|
||||
* @returns {{ bg: string, border: string, text: string }}
|
||||
*/
|
||||
export function eventColor(uid) {
|
||||
return PALETTE[hashStr(uid) % PALETTE.length]
|
||||
}
|
||||
@@ -56,3 +56,36 @@ export function nowCompactTimestamp() {
|
||||
export function toHHMM(date) {
|
||||
return `${pad2(date.getHours())}:${pad2(date.getMinutes())}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 isoDate 所在周的周一 (ISO 周, 周一为首日)。
|
||||
* @param {string} isoDate - 'YYYY-MM-DD'
|
||||
* @returns {string} 周一的 'YYYY-MM-DD'
|
||||
*/
|
||||
export function startOfWeek(isoDate) {
|
||||
const d = toDate(isoDate)
|
||||
const dow = d.getDay() // 0=周日 .. 6=周六
|
||||
const diff = dow === 0 ? -6 : 1 - dow // 回退到周一
|
||||
d.setDate(d.getDate() + diff)
|
||||
return toISODate(d)
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 isoDate 所在月的第一天。
|
||||
* @param {string} isoDate - 'YYYY-MM-DD'
|
||||
* @returns {string} 当月 1 号的 'YYYY-MM-DD'
|
||||
*/
|
||||
export function startOfMonth(isoDate) {
|
||||
const { year, month } = parseISODate(isoDate)
|
||||
return `${year}-${pad2(month)}-01`
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 'HH:MM' 转换为当天分钟数 (0~1439)。
|
||||
* @param {string} hhmm - 'HH:MM'
|
||||
* @returns {number}
|
||||
*/
|
||||
export function hhmmToMinutes(hhmm) {
|
||||
const [h, m] = hhmm.split(':').map(Number)
|
||||
return h * 60 + m
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user