feat: 月预览自定义悬停 tooltip, 月/周预览添加回到今日按钮
- MonthPreview: 用自定义 tooltip 替代原生 title 属性, 鼠标悬停事件 即时显示详细信息 (标题/时间/地点/描述), 固定在右下角避免遮挡 - MonthPreview/WeekPreview: 工具栏新增回到今日按钮, 一键跳转回当天 - useCalendar: 新增 goToToday() action, 重置 previewDate 为今天
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { useCalendar } from '../composables/useCalendar.js'
|
import { useCalendar } from '../composables/useCalendar.js'
|
||||||
import { expandOccurrences } from '../lib/recur.js'
|
import { expandOccurrences } from '../lib/recur.js'
|
||||||
import { eventColor } from '../lib/colors.js'
|
import { eventColor } from '../lib/colors.js'
|
||||||
@@ -8,11 +8,14 @@ import {
|
|||||||
} from '../lib/date.js'
|
} from '../lib/date.js'
|
||||||
import { DOW } from '../lib/weekday.js'
|
import { DOW } from '../lib/weekday.js'
|
||||||
|
|
||||||
const { state, prevMonth, nextMonth } = useCalendar()
|
const { state, prevMonth, nextMonth, goToToday } = useCalendar()
|
||||||
|
|
||||||
// 表头: 周一~周日
|
// 表头: 周一~周日
|
||||||
const headers = DOW.slice(1).concat(DOW[0])
|
const headers = DOW.slice(1).concat(DOW[0])
|
||||||
|
|
||||||
|
// 自定义 tooltip 状态
|
||||||
|
const hoverEvent = ref(null)
|
||||||
|
|
||||||
// 当前预览月的年月标签
|
// 当前预览月的年月标签
|
||||||
const monthLabel = computed(function () {
|
const monthLabel = computed(function () {
|
||||||
const parts = parseISODate(startOfMonth(state.previewDate))
|
const parts = parseISODate(startOfMonth(state.previewDate))
|
||||||
@@ -62,12 +65,12 @@ function colorStyle(uid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function tooltip(ev) {
|
function showTooltip(ev) {
|
||||||
const parts = [ev.summary || '(无标题)']
|
hoverEvent.value = ev
|
||||||
parts.push(ev.dtstartTime + ' - ' + ev.dtendTime)
|
}
|
||||||
if (ev.location) parts.push('地点: ' + ev.location)
|
|
||||||
if (ev.description) parts.push('描述: ' + ev.description)
|
function hideTooltip() {
|
||||||
return parts.join('\n')
|
hoverEvent.value = null
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -77,6 +80,7 @@ function tooltip(ev) {
|
|||||||
<button class="nav-btn" @click="prevMonth">上个月</button>
|
<button class="nav-btn" @click="prevMonth">上个月</button>
|
||||||
<span class="month-label">{{ monthLabel }}</span>
|
<span class="month-label">{{ monthLabel }}</span>
|
||||||
<button class="nav-btn" @click="nextMonth">下个月</button>
|
<button class="nav-btn" @click="nextMonth">下个月</button>
|
||||||
|
<button class="nav-btn today-btn" @click="goToToday">回到今日</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="month-grid">
|
<div class="month-grid">
|
||||||
<div v-for="h in headers" :key="h" class="grid-header">{{ h }}</div>
|
<div v-for="h in headers" :key="h" class="grid-header">{{ h }}</div>
|
||||||
@@ -96,13 +100,21 @@ function tooltip(ev) {
|
|||||||
:key="occ.event.uid + occ.date"
|
:key="occ.event.uid + occ.date"
|
||||||
class="event-chip"
|
class="event-chip"
|
||||||
:style="colorStyle(occ.event.uid)"
|
:style="colorStyle(occ.event.uid)"
|
||||||
:title="tooltip(occ.event)"
|
@mouseenter="showTooltip(occ.event)"
|
||||||
|
@mouseleave="hideTooltip"
|
||||||
>
|
>
|
||||||
{{ occ.event.summary || '(无标题)' }}
|
{{ occ.event.summary || '(无标题)' }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 自定义 tooltip -->
|
||||||
|
<div v-if="hoverEvent" class="event-tooltip" @mouseenter="showTooltip(hoverEvent)" @mouseleave="hideTooltip">
|
||||||
|
<div class="tooltip-title">{{ hoverEvent.summary || '(无标题)' }}</div>
|
||||||
|
<div class="tooltip-row">时间: {{ hoverEvent.dtstartTime }} - {{ hoverEvent.dtendTime }}</div>
|
||||||
|
<div v-if="hoverEvent.location" class="tooltip-row">地点: {{ hoverEvent.location }}</div>
|
||||||
|
<div v-if="hoverEvent.description" class="tooltip-desc">{{ hoverEvent.description }}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -112,6 +124,7 @@ function tooltip(ev) {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.preview-toolbar {
|
.preview-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -130,6 +143,8 @@ function tooltip(ev) {
|
|||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
}
|
}
|
||||||
.nav-btn:hover { background: #ecf0f1; }
|
.nav-btn:hover { background: #ecf0f1; }
|
||||||
|
.today-btn { border-color: #3498db; color: #3498db; font-weight: 600; }
|
||||||
|
.today-btn:hover { background: #eaf4fc; }
|
||||||
.month-label {
|
.month-label {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -200,4 +215,33 @@ function tooltip(ev) {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
.event-tooltip {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 16px;
|
||||||
|
right: 16px;
|
||||||
|
max-width: 320px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #d5dbdb;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
padding: 12px 16px;
|
||||||
|
z-index: 100;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.tooltip-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.tooltip-row {
|
||||||
|
color: #34495e;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
.tooltip-desc {
|
||||||
|
color: #7f8c8d;
|
||||||
|
margin-top: 6px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from '../lib/date.js'
|
} from '../lib/date.js'
|
||||||
import { DOW } from '../lib/weekday.js'
|
import { DOW } from '../lib/weekday.js'
|
||||||
|
|
||||||
const { state, prevWeek, nextWeek } = useCalendar()
|
const { state, prevWeek, nextWeek, goToToday } = useCalendar()
|
||||||
|
|
||||||
const HOUR_HEIGHT = 48 // 每小时像素高度
|
const HOUR_HEIGHT = 48 // 每小时像素高度
|
||||||
const headers = DOW.slice(1).concat(DOW[0]) // 周一..周日
|
const headers = DOW.slice(1).concat(DOW[0]) // 周一..周日
|
||||||
@@ -113,6 +113,7 @@ function tooltip(ev) {
|
|||||||
<button class="nav-btn" @click="prevWeek">上一周</button>
|
<button class="nav-btn" @click="prevWeek">上一周</button>
|
||||||
<span class="week-label">{{ weekLabel }}</span>
|
<span class="week-label">{{ weekLabel }}</span>
|
||||||
<button class="nav-btn" @click="nextWeek">下一周</button>
|
<button class="nav-btn" @click="nextWeek">下一周</button>
|
||||||
|
<button class="nav-btn today-btn" @click="goToToday">回到今日</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="week-grid-wrapper">
|
<div class="week-grid-wrapper">
|
||||||
<div class="week-grid">
|
<div class="week-grid">
|
||||||
@@ -177,6 +178,8 @@ function tooltip(ev) {
|
|||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
}
|
}
|
||||||
.nav-btn:hover { background: #ecf0f1; }
|
.nav-btn:hover { background: #ecf0f1; }
|
||||||
|
.today-btn { border-color: #3498db; color: #3498db; font-weight: 600; }
|
||||||
|
.today-btn:hover { background: #eaf4fc; }
|
||||||
.week-label {
|
.week-label {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ function nextWeek() {
|
|||||||
state.previewDate = addDays(state.previewDate, 7)
|
state.previewDate = addDays(state.previewDate, 7)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goToToday() {
|
||||||
|
state.previewDate = todayISO()
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------ //
|
// ------------------------------------------------------------------ //
|
||||||
// 计算属性
|
// 计算属性
|
||||||
// ------------------------------------------------------------------ //
|
// ------------------------------------------------------------------ //
|
||||||
@@ -211,5 +215,6 @@ export function useCalendar() {
|
|||||||
nextMonth,
|
nextMonth,
|
||||||
prevWeek,
|
prevWeek,
|
||||||
nextWeek,
|
nextWeek,
|
||||||
|
goToToday,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user