feat: add DropZone and App layout skeleton
This commit is contained in:
45
src/App.vue
45
src/App.vue
@@ -1,9 +1,48 @@
|
||||
<script setup>
|
||||
import { useCalendar } from './composables/useCalendar.js'
|
||||
import DropZone from './components/DropZone.vue'
|
||||
|
||||
const { isLoaded } = useCalendar()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="font-family: sans-serif; padding: 40px;">
|
||||
<h1>📅 ICS 日程整理器</h1>
|
||||
<p>脚手架就绪</p>
|
||||
<div class="app">
|
||||
<header class="app-header">
|
||||
<h1>📅 ICS 日程整理器</h1>
|
||||
</header>
|
||||
<main class="app-main">
|
||||
<DropZone v-if="!isLoaded" />
|
||||
<div v-else class="placeholder">
|
||||
<p>文件已加载(列表与编辑器待实现)</p>
|
||||
</div>
|
||||
</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;
|
||||
}
|
||||
.app-header h1 { font-size: 18px; font-weight: 600; }
|
||||
.app-main { flex: 1; display: flex; overflow: hidden; }
|
||||
.placeholder {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #95a5a6;
|
||||
}
|
||||
</style>
|
||||
|
||||
99
src/components/DropZone.vue
Normal file
99
src/components/DropZone.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useCalendar } from '../composables/useCalendar.js'
|
||||
|
||||
const { loadFile } = useCalendar()
|
||||
const dragOver = ref(false)
|
||||
const error = ref('')
|
||||
|
||||
function onDrop(e) {
|
||||
dragOver.value = false
|
||||
error.value = ''
|
||||
const file = e.dataTransfer.files[0]
|
||||
if (!file) return
|
||||
if (!file.name.toLowerCase().endsWith('.ics')) {
|
||||
error.value = '请选择 .ics 文件'
|
||||
return
|
||||
}
|
||||
loadFile(file).catch((err) => {
|
||||
error.value = '解析失败:' + (err.message || err)
|
||||
})
|
||||
}
|
||||
|
||||
function onPick(e) {
|
||||
error.value = ''
|
||||
const file = e.target.files[0]
|
||||
if (!file) return
|
||||
loadFile(file).catch((err) => {
|
||||
error.value = '解析失败:' + (err.message || err)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="dropzone"
|
||||
:class="{ active: dragOver }"
|
||||
@dragover.prevent="dragOver = true"
|
||||
@dragleave.prevent="dragOver = false"
|
||||
@drop.prevent="onDrop"
|
||||
>
|
||||
<div class="dropzone-content">
|
||||
<div class="icon">📅</div>
|
||||
<p class="hint">拖入 .ics 文件,或</p>
|
||||
<label class="pick-btn">
|
||||
点击选择文件
|
||||
<input type="file" accept=".ics" @change="onPick" hidden />
|
||||
</label>
|
||||
<p v-if="error" class="error">{{ error }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dropzone {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2px dashed #bdc3c7;
|
||||
border-radius: 12px;
|
||||
margin: 40px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.dropzone.active {
|
||||
border-color: #3498db;
|
||||
background: #eaf4fc;
|
||||
}
|
||||
.dropzone-content {
|
||||
text-align: center;
|
||||
}
|
||||
.icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.hint {
|
||||
color: #7f8c8d;
|
||||
font-size: 14px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.pick-btn {
|
||||
display: inline-block;
|
||||
padding: 10px 24px;
|
||||
background: #3498db;
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.pick-btn:hover {
|
||||
background: #2980b9;
|
||||
}
|
||||
.error {
|
||||
color: #e74c3c;
|
||||
font-size: 13px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user