feat: add DropZone and App layout skeleton

This commit is contained in:
timeTable2 dev
2026-07-13 18:39:15 +08:00
parent 361cc38384
commit 906679fc11
2 changed files with 141 additions and 3 deletions

View 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>