ical-io.js: - Import pad2/parseISODate from date.js (remove local duplicates) - Rename ICAL.Time helpers to icalTimeTo* for clarity useCalendar.js: - Delegate addEvent to createEvent() factory - Delegate splitEvent to splitRecurringEvent() pure function - Use cloneEvent() instead of JSON.parse(JSON.stringify()) - Add selectEvent() action (components no longer mutate state.selectedUid directly) Components: - EventList: use selectEvent action + toDate() from date.js - HeaderBar: destructure loadFile at top level (was re-calling useCalendar in handler) - EventDetail: import DOW from weekday.js (was inlining the array) - OccurrenceGrid: use toDate() from date.js
76 lines
1.6 KiB
Vue
76 lines
1.6 KiB
Vue
<script setup>
|
||
import { computed } from 'vue'
|
||
import { DOW } from '../lib/weekday.js'
|
||
import { toDate } from '../lib/date.js'
|
||
import { expandOccurrences } from '../lib/recur.js'
|
||
|
||
const props = defineProps({
|
||
event: { type: Object, required: true },
|
||
})
|
||
const emit = defineEmits(['toggle'])
|
||
|
||
const occurrences = computed(() => expandOccurrences(props.event))
|
||
|
||
function dowFor(dateStr) {
|
||
return DOW[toDate(dateStr).getDay()]
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div>
|
||
<p class="hint">
|
||
点击日期切换"排除/包含"状态。排除的日期(红色删除线)会写入 EXDATE。
|
||
</p>
|
||
<div class="occ-grid">
|
||
<div
|
||
v-for="occ in occurrences"
|
||
:key="occ.date"
|
||
class="occ"
|
||
:class="occ.skipped ? 'skipped' : 'active'"
|
||
@click="emit('toggle', occ.date)"
|
||
>
|
||
{{ occ.date }}<br />
|
||
<span class="dow">{{ dowFor(occ.date) }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.hint {
|
||
font-size: 12px;
|
||
color: #7f8c8d;
|
||
margin-bottom: 8px;
|
||
}
|
||
.occ-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||
gap: 8px;
|
||
max-height: 360px;
|
||
overflow-y: auto;
|
||
padding: 4px;
|
||
}
|
||
.occ {
|
||
padding: 8px 10px;
|
||
border: 1px solid #d5dbdb;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
text-align: center;
|
||
transition: all 0.12s;
|
||
}
|
||
.occ:hover { border-color: #3498db; }
|
||
.occ.skipped {
|
||
background: #fce4ec;
|
||
border-color: #e74c3c;
|
||
color: #c0392b;
|
||
text-decoration: line-through;
|
||
}
|
||
.occ.active {
|
||
background: #e8f5e9;
|
||
border-color: #27ae60;
|
||
color: #1e7d32;
|
||
}
|
||
.dow { font-size: 10px; color: #95a5a6; }
|
||
</style>
|