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