Files
timeTableFix/src/components/HeaderBar.vue
zikai 1c6ecf9d86 refactor: 移除重做/应用按钮, 自动应用更改, 删除死代码, 修复性能问题
UI 变更:
- 移除重做按钮及 redo/canRedo/redoStack 相关逻辑 (useCalendar, HeaderBar)
- 移除应用更改按钮, EventDetail 改为 watch form 自动应用 (deep watch)
- 用 syncing 标志防止 syncForm 回写时触发自动应用的循环

死代码清理:
- weekday.js: 移除未使用的 BYDAY_TO_INDEX 和 dowLabel
- date.js: 移除未使用的 toHHMM
- App.vue: 移除未使用的 .empty CSS
- DropZone.vue: 移除空的 .icon 容器及 CSS
- test/weekday.test.js: 移除对应死代码的 2 个测试用例

性能修复:
- WeekPreview: layoutEvents 从模板内联调用改为 computed (laidOutByDate),
  避免每次渲染重新计算布局
- MonthPreview: occByDate 过滤为仅网格内 42 天, 避免展开全部 occurrence

代码质量:
- useCalendar: prevMonth/nextMonth 合并为 shiftMonth(delta), 消除重复
- WeekPreview/MonthPreview: 移除未使用导入 (toISODate, toDate)
2026-07-22 04:18:05 +00:00

76 lines
2.4 KiB
Vue

<script setup>
import { useCalendar } from '../composables/useCalendar.js'
import { ref } from 'vue'
const { state, canUndo, organize, undo, serialize, markSaved, loadFile } = useCalendar()
const fileInput = ref(null)
function onOrganize() {
const stats = organize()
if (stats && stats.series === 0) {
alert('未发现可合并的重复模式')
}
}
function onImport() {
fileInput.value?.click()
}
function onFilePicked(e) {
const file = e.target.files[0]
if (!file) return
loadFile(file).catch((err) => alert('解析失败:' + (err.message || err)))
e.target.value = ''
}
function onDownload() {
if (state.dirty && !confirm('有未保存的更改,仍要下载?')) return
const text = serialize()
const blob = new Blob([text], { type: 'text/calendar' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
const base = state.fileName ? state.fileName.replace(/\.ics$/i, '') : 'calendar'
a.href = url
a.download = base + '.edited.ics'
a.click()
URL.revokeObjectURL(url)
markSaved()
}
</script>
<template>
<div class="header-actions">
<button class="btn btn-primary" @click="onOrganize" :disabled="state.events.length === 0">
整理去重
</button>
<button class="btn btn-secondary" @click="undo" :disabled="!canUndo">撤销</button>
<button class="btn btn-secondary" @click="onImport">导入文件</button>
<button class="btn btn-success" @click="onDownload" :disabled="state.events.length === 0">
下载 ICS
</button>
<span v-if="state.dirty" class="dirty-dot" title="有未保存更改"></span>
<input ref="fileInput" type="file" accept=".ics" @change="onFilePicked" hidden />
</div>
</template>
<style scoped>
.header-actions { display: flex; gap: 10px; align-items: center; }
.btn {
padding: 7px 16px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: background 0.15s;
}
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-primary { background: #3498db; color: #fff; }
.btn-primary:hover:not(:disabled) { background: #2980b9; }
.btn-success { background: #27ae60; color: #fff; }
.btn-success:hover:not(:disabled) { background: #229954; }
.btn-secondary { background: #ecf0f1; color: #2c3e50; }
.btn-secondary:hover:not(:disabled) { background: #d5dbdb; }
.dirty-dot { color: #e74c3c; font-size: 16px; }
</style>