auto close seletor
This commit is contained in:
35
src/App.vue
35
src/App.vue
@@ -60,11 +60,32 @@ let store: any = null;
|
|||||||
let captureUnlisten: any = null;
|
let captureUnlisten: any = null;
|
||||||
|
|
||||||
// --- Custom Picker States ---
|
// --- Custom Picker States ---
|
||||||
|
const calendarRef = ref<HTMLElement | null>(null);
|
||||||
|
const startTimePickerRef = ref<HTMLElement | null>(null);
|
||||||
|
const endTimePickerRef = ref<HTMLElement | null>(null);
|
||||||
|
const tagSelectRef = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
const isCalendarOpen = ref(false);
|
const isCalendarOpen = ref(false);
|
||||||
const calendarMonth = ref(new Date());
|
const calendarMonth = ref(new Date());
|
||||||
const isStartTimeOpen = ref(false);
|
const isStartTimeOpen = ref(false);
|
||||||
const isEndTimeOpen = ref(false);
|
const isEndTimeOpen = ref(false);
|
||||||
|
|
||||||
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
if (isCalendarOpen.value && calendarRef.value && !calendarRef.value.contains(target)) {
|
||||||
|
isCalendarOpen.value = false;
|
||||||
|
}
|
||||||
|
if (isStartTimeOpen.value && startTimePickerRef.value && !startTimePickerRef.value.contains(target)) {
|
||||||
|
isStartTimeOpen.value = false;
|
||||||
|
}
|
||||||
|
if (isEndTimeOpen.value && endTimePickerRef.value && !endTimePickerRef.value.contains(target)) {
|
||||||
|
isEndTimeOpen.value = false;
|
||||||
|
}
|
||||||
|
if (isTagSelectOpen.value && tagSelectRef.value && !tagSelectRef.value.contains(target)) {
|
||||||
|
isTagSelectOpen.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const openStartTimePicker = async () => {
|
const openStartTimePicker = async () => {
|
||||||
isStartTimeOpen.value = !isStartTimeOpen.value;
|
isStartTimeOpen.value = !isStartTimeOpen.value;
|
||||||
isEndTimeOpen.value = false;
|
isEndTimeOpen.value = false;
|
||||||
@@ -105,6 +126,7 @@ const selectCalendarDate = (date: Date) => {
|
|||||||
|
|
||||||
// --- Logic ---
|
// --- Logic ---
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
window.addEventListener('mousedown', handleClickOutside);
|
||||||
store = await load("config.json");
|
store = await load("config.json");
|
||||||
const path = await store.get("savePath");
|
const path = await store.get("savePath");
|
||||||
const dPath = await store.get("dbPath");
|
const dPath = await store.get("dbPath");
|
||||||
@@ -125,7 +147,10 @@ onMounted(async () => {
|
|||||||
captureUnlisten = await listen("refresh-timeline", () => { loadTimeline(); });
|
captureUnlisten = await listen("refresh-timeline", () => { loadTimeline(); });
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => { if (captureUnlisten) captureUnlisten(); });
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
if (captureUnlisten) captureUnlisten();
|
||||||
|
});
|
||||||
|
|
||||||
const selectFolder = async () => { const s = await open({ directory: true }); if (s) savePath.value = s as string; };
|
const selectFolder = async () => { const s = await open({ directory: true }); if (s) savePath.value = s as string; };
|
||||||
const selectDBFile = async () => { const s = await open({ filters: [{ name: "SQLite", extensions: ["db"] }] }); if (s) dbPath.value = s as string; };
|
const selectDBFile = async () => { const s = await open({ filters: [{ name: "SQLite", extensions: ["db"] }] }); if (s) dbPath.value = s as string; };
|
||||||
@@ -355,7 +380,7 @@ const isTagSelectOpen = ref(false);
|
|||||||
<div class="w-80 bg-[#F8FAFD] border-r border-[#E5E5E7] flex flex-col select-none relative">
|
<div class="w-80 bg-[#F8FAFD] border-r border-[#E5E5E7] flex flex-col select-none relative">
|
||||||
<div class="p-6 bg-[#F8FAFD]/80 backdrop-blur-md sticky top-0 z-20 border-b border-[#E5E5E7]/50">
|
<div class="p-6 bg-[#F8FAFD]/80 backdrop-blur-md sticky top-0 z-20 border-b border-[#E5E5E7]/50">
|
||||||
<div class="flex items-center justify-between mb-4"><h2 class="text-lg font-bold">历史活动</h2><button @click="loadTimeline(true); loadEvents()" class="p-2 hover:bg-white rounded-xl text-[#86868B]"><RefreshCw :size="18" /></button></div>
|
<div class="flex items-center justify-between mb-4"><h2 class="text-lg font-bold">历史活动</h2><button @click="loadTimeline(true); loadEvents()" class="p-2 hover:bg-white rounded-xl text-[#86868B]"><RefreshCw :size="18" /></button></div>
|
||||||
<div class="relative group">
|
<div ref="calendarRef" class="relative group">
|
||||||
<button @click="isCalendarOpen = !isCalendarOpen" class="w-full bg-white border border-[#E5E5E7] rounded-xl pl-11 pr-4 py-2.5 text-sm font-bold text-left flex items-center hover:bg-[#F2F2F7] transition-all">
|
<button @click="isCalendarOpen = !isCalendarOpen" class="w-full bg-white border border-[#E5E5E7] rounded-xl pl-11 pr-4 py-2.5 text-sm font-bold text-left flex items-center hover:bg-[#F2F2F7] transition-all">
|
||||||
<Calendar :size="16" class="absolute left-4 text-[#86868B]" />
|
<Calendar :size="16" class="absolute left-4 text-[#86868B]" />
|
||||||
{{ currentDate }}
|
{{ currentDate }}
|
||||||
@@ -415,7 +440,7 @@ const isTagSelectOpen = ref(false);
|
|||||||
<div class="p-10 space-y-8">
|
<div class="p-10 space-y-8">
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-4">
|
||||||
<!-- Start Time Custom Picker -->
|
<!-- Start Time Custom Picker -->
|
||||||
<div class="flex-1 space-y-1 relative">
|
<div ref="startTimePickerRef" class="flex-1 space-y-1 relative">
|
||||||
<label class="text-[10px] font-bold text-[#86868B]">开始时间</label>
|
<label class="text-[10px] font-bold text-[#86868B]">开始时间</label>
|
||||||
<button @click="openStartTimePicker"
|
<button @click="openStartTimePicker"
|
||||||
class="w-full h-12 bg-[#F2F2F7] rounded-xl px-4 flex items-center justify-center gap-2 hover:bg-[#E5E5E7] transition-all border border-transparent focus:border-[#007AFF]/30">
|
class="w-full h-12 bg-[#F2F2F7] rounded-xl px-4 flex items-center justify-center gap-2 hover:bg-[#E5E5E7] transition-all border border-transparent focus:border-[#007AFF]/30">
|
||||||
@@ -443,7 +468,7 @@ const isTagSelectOpen = ref(false);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End Time Custom Picker -->
|
<!-- End Time Custom Picker -->
|
||||||
<div class="flex-1 space-y-1 relative">
|
<div ref="endTimePickerRef" class="flex-1 space-y-1 relative">
|
||||||
<label class="text-[10px] font-bold text-[#86868B]">结束时间</label>
|
<label class="text-[10px] font-bold text-[#86868B]">结束时间</label>
|
||||||
<button @click="openEndTimePicker"
|
<button @click="openEndTimePicker"
|
||||||
class="w-full h-12 bg-[#F2F2F7] rounded-xl px-4 flex items-center justify-center gap-2 hover:bg-[#E5E5E7] transition-all border border-transparent focus:border-[#007AFF]/30">
|
class="w-full h-12 bg-[#F2F2F7] rounded-xl px-4 flex items-center justify-center gap-2 hover:bg-[#E5E5E7] transition-all border border-transparent focus:border-[#007AFF]/30">
|
||||||
@@ -504,7 +529,7 @@ const isTagSelectOpen = ref(false);
|
|||||||
<label class="text-[10px] font-bold text-[#86868B] ml-1">标签名称</label>
|
<label class="text-[10px] font-bold text-[#86868B] ml-1">标签名称</label>
|
||||||
<input v-model="newTagName" placeholder="输入名称..." class="w-full bg-white rounded-xl px-4 py-2.5 text-sm outline-none border border-transparent focus:border-[#007AFF] transition-all" />
|
<input v-model="newTagName" placeholder="输入名称..." class="w-full bg-white rounded-xl px-4 py-2.5 text-sm outline-none border border-transparent focus:border-[#007AFF] transition-all" />
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-1 relative">
|
<div ref="tagSelectRef" class="space-y-1 relative">
|
||||||
<label class="text-[10px] font-bold text-[#86868B] ml-1">父级标签</label>
|
<label class="text-[10px] font-bold text-[#86868B] ml-1">父级标签</label>
|
||||||
<button @click="isTagSelectOpen = !isTagSelectOpen" class="w-full bg-white rounded-xl px-4 py-2.5 text-sm text-left flex justify-between items-center border border-transparent focus:border-[#007AFF] transition-all">
|
<button @click="isTagSelectOpen = !isTagSelectOpen" class="w-full bg-white rounded-xl px-4 py-2.5 text-sm text-left flex justify-between items-center border border-transparent focus:border-[#007AFF] transition-all">
|
||||||
<span>{{ getTagName(newTagParent) }}</span>
|
<span>{{ getTagName(newTagParent) }}</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user