- organize: seriesKey 移除 weekday, 解锁每3天等 DAILY 序列合并 - organize: fitSeries 放宽阈值(稀疏序列如 BUS LAW Workshop 合并) - organize: 新增 splitIntoChains, GCD=1 的交错多班次拆为多个系列 - useCalendar: 移除 dirty/history/undo/snapshot/markSaved 死代码 - HeaderBar: 移除撤销按钮与红点●, 下载兜底序列化当前状态 - 测试: 42 项(原32), 覆盖 DAILY/稀疏/交错拆分/两两和候选等场景 - 文档: README 精简, 新增 docs/organize-algorithm.md 与 auto-save-design.md - 保留移动端 CSS 适配
112 lines
2.9 KiB
Vue
112 lines
2.9 KiB
Vue
<script setup>
|
|
import { useCalendar } from './composables/useCalendar.js'
|
|
import DropZone from './components/DropZone.vue'
|
|
import HeaderBar from './components/HeaderBar.vue'
|
|
import EventList from './components/EventList.vue'
|
|
import EventDetail from './components/EventDetail.vue'
|
|
import MonthPreview from './components/MonthPreview.vue'
|
|
import WeekPreview from './components/WeekPreview.vue'
|
|
|
|
const { isLoaded, state, setViewMode } = useCalendar()
|
|
</script>
|
|
|
|
<template>
|
|
<div class="app">
|
|
<header class="app-header">
|
|
<div class="header-left">
|
|
<h1>ICS 日程整理器</h1>
|
|
<div v-if="isLoaded" class="view-switcher">
|
|
<button
|
|
:class="{ active: state.viewMode === 'edit' }"
|
|
@click="setViewMode('edit')"
|
|
>编辑</button>
|
|
<button
|
|
:class="{ active: state.viewMode === 'month' }"
|
|
@click="setViewMode('month')"
|
|
>月预览</button>
|
|
<button
|
|
:class="{ active: state.viewMode === 'week' }"
|
|
@click="setViewMode('week')"
|
|
>周预览</button>
|
|
</div>
|
|
</div>
|
|
<HeaderBar />
|
|
</header>
|
|
<main class="app-main">
|
|
<DropZone v-if="!isLoaded" />
|
|
<template v-else>
|
|
<EventList v-if="state.viewMode === 'edit'" />
|
|
<div class="detail-area">
|
|
<EventDetail v-if="state.viewMode === 'edit'" />
|
|
<MonthPreview v-else-if="state.viewMode === 'month'" />
|
|
<WeekPreview v-else-if="state.viewMode === 'week'" />
|
|
</div>
|
|
</template>
|
|
</main>
|
|
</div>
|
|
</template>
|
|
|
|
<style>
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body {
|
|
font-family: -apple-system, "Segoe UI", Roboto, "Microsoft YaHei", sans-serif;
|
|
background: #f5f6f8;
|
|
color: #222;
|
|
line-height: 1.5;
|
|
}
|
|
.app { min-height: 100vh; display: flex; flex-direction: column; }
|
|
.app-header {
|
|
background: #2c3e50;
|
|
color: #fff;
|
|
padding: 14px 24px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
.header-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 24px;
|
|
}
|
|
.app-header h1 { font-size: 18px; font-weight: 600; white-space: nowrap; }
|
|
.view-switcher {
|
|
display: flex;
|
|
background: #1a252f;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
}
|
|
.view-switcher button {
|
|
background: transparent;
|
|
color: #bdc3c7;
|
|
border: none;
|
|
padding: 6px 16px;
|
|
font-size: 13px;
|
|
cursor: pointer;
|
|
transition: all 0.15s;
|
|
}
|
|
.view-switcher button:hover { color: #fff; }
|
|
.view-switcher button.active {
|
|
background: #3498db;
|
|
color: #fff;
|
|
}
|
|
.app-main { flex: 1; display: flex; overflow: hidden; }
|
|
.detail-area { flex: 1; overflow-y: auto; }
|
|
|
|
/* 移动端适配 */
|
|
@media (max-width: 768px) {
|
|
.app-header {
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
padding: 10px 12px;
|
|
}
|
|
.header-left {
|
|
width: 100%;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
.app-header h1 { font-size: 15px; }
|
|
.view-switcher button { padding: 5px 10px; font-size: 12px; }
|
|
.app-main { flex-direction: column; }
|
|
}
|
|
</style>
|