Files
schedule/src/App.vue

481 lines
13 KiB
Vue
Raw Normal View History

2025-06-10 17:03:02 +08:00
<template>
<div class="min-h-screen w-full bg-gradient-to-br from-gray-50 to-gray-100">
<!-- 登录模态框 -->
<LoginModal v-if="showLoginModal" @login="handleLogin" @close="showLoginModal = false" />
<!-- 顶部导航 - 粘性布局 -->
<header class="sticky top-0 z-50 bg-gradient-to-r from-primary-600 to-primary-800 py-5 px-6 rounded-b-2xl shadow-lg">
<div class="max-w-7xl mx-auto">
<div class="flex flex-col sm:flex-row items-center justify-between">
<div class="flex items-center">
<div class="w-12 h-12 rounded-xl bg-white bg-opacity-20 flex items-center justify-center shadow-md">
<i class="fas fa-calendar-alt text-b text-2xl"></i>
</div>
<h1 class="ml-4 text-2xl font-bold text-b drop-shadow">智能日程管家</h1>
</div>
<div class="mt-4 sm:mt-0 flex items-center">
<div class="flex rounded-xl overflow-hidden bg-white bg-opacity-20">
<button
@click="changeView('day')"
:class="{'bg-white text-primary-600': view === 'day'}"
class="px-4 py-2 font-medium transition-all hover:transform hover:-translate-y-0.5"
>
<i class="fas fa-sun mr-2"></i> 日视图
</button>
<button
@click="changeView('week')"
:class="{'bg-white text-primary-600': view === 'week'}"
class="px-4 py-2 font-medium transition-all hover:transform hover:-translate-y-0.5"
>
<i class="fas fa-calendar-week mr-2"></i> 周视图
</button>
<button
@click="changeView('month')"
:class="{'bg-white text-primary-600': view === 'month'}"
class="px-4 py-2 font-medium transition-all hover:transform hover:-translate-y-0.5"
>
<i class="fas fa-calendar-days mr-2"></i> 月视图
</button>
</div>
<div class="ml-4 flex items-center">
<div class="w-10 h-10 rounded-full overflow-hidden border-2 border-white">
</div>
<span class="ml-2 text-white text-sm hidden md:block">{{ user.name }}</span>
</div>
</div>
</div>
</div>
</header>
<div class="max-w-7xl mx-auto px-4 py-8 sm:px-6">
<!-- 日历导航 -->
<div data-aos="fade-up" class="flex items-center justify-between mb-8 bg-white rounded-xl p-4 shadow">
<div class="flex items-center">
<button
@click="changeDate(-1)"
class="w-10 h-10 flex items-center justify-center rounded-full bg-white hover:bg-gray-100 transition-all shadow hover:scale-105"
>
<i class="fas fa-chevron-left text-primary-600"></i>
</button>
<div class="mx-4">
<div class="text-xl font-bold text-gray-800 flex items-center">
{{ displayDate }}
<span class="ml-3 text-sm px-3 py-1 bg-primary-600 text-white rounded-full">当前视图</span>
</div>
<p class="text-gray-600 mt-1">{{ dayjs().format('YYYY年MM月') }}</p>
</div>
<button
@click="changeDate(1)"
class="w-10 h-10 flex items-center justify-center rounded-full bg-white hover:bg-gray-100 transition-all shadow hover:scale-105"
>
<i class="fas fa-chevron-right text-primary-600"></i>
</button>
</div>
<button
@click="currentDate = dayjs().format('YYYY-MM-DD')"
class="flex items-center px-4 py-2 bg-gradient-to-r from-primary-500 to-primary-700 hover:from-primary-600 hover:to-primary-800 text-white rounded-xl transition-all shadow-lg hover:shadow-xl hover:transform hover:-translate-y-0.5"
>
<i class="fas fa-calendar-check mr-2"></i> 返回今天
</button>
</div>
<!-- 主要内容 -->
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
<!-- 左侧面板 -->
<div class="lg:col-span-1" data-aos="fade-right" data-aos-delay="100">
<CommonEvents
:commonEvents="commonEvents"
@add-common-event="showCommonEventModal = true"
@drag-start="handleDragStart"
@drag-end="handleDragEnd"
/>
</div>
<!-- 中间面板 - 日历 -->
<div class="lg:col-span-2" data-aos="fade-up" data-aos-delay="200">
<CalendarView
:view="view"
:currentDate="currentDate"
:selectedDate="selectedDate"
:events="events"
:commonEvents="commonEvents"
@select-date="selectDate"
@show-event-detail="showEventDetail"
@drop-event="handleDropEvent"
@drag-start="handleDragStart"
@drag-end="handleDragEnd"
/>
</div>
<!-- 右侧面板 - 详情 -->
<div class="lg:col-span-1" data-aos="fade-left" data-aos-delay="300">
<EventDetail
:selectedDate="selectedDate"
:events="events"
:commonEvents="commonEvents"
@add-event="showEventModal = true"
@edit-event="showEventDetail"
@drop-event="handleDropEvent"
@drag-start="handleDragStart"
@drag-end="handleDragEnd"
/>
</div>
</div>
</div>
<!-- 模态框 -->
<EventModal
v-if="showEventModal"
:event="editingEvent || newEvent"
:isEditing="!!editingEvent"
@close="closeModal"
@save="saveEvent"
@delete="deleteEvent"
/>
<CommonEventModal
v-if="showCommonEventModal"
@close="closeCommonModal"
@save="saveCommonEvent"
/>
</div>
</template>
2025-04-24 13:39:24 +08:00
<script setup>
2025-06-10 17:03:02 +08:00
import { ref, computed, onMounted } from 'vue';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import CalendarView from './components/CalendarView.vue';
import EventDetail from './components/EventDetail.vue';
import CommonEvents from './components/CommonEvents.vue';
import EventModal from './components/EventModal.vue';
import CommonEventModal from './components/CommonEventModal.vue';
import LoginModal from './components/LoginModal.vue';
dayjs.locale('zh-cn');
// 状态管理
const view = ref('month');
const currentDate = ref(dayjs().format('YYYY-MM-DD'));
const selectedDate = ref(dayjs().format('YYYY-MM-DD'));
const showEventModal = ref(false);
const showCommonEventModal = ref(false);
const showLoginModal = ref(true); // 默认显示登录模态框
const editingEvent = ref(null);
// 用户信息
const user = ref({
id: '',
name: '',
avatar: '',
phone: '',
email: '',
team: '萧康云医技术开发团队-医生组'
});
// 数据
const events = ref([
{
id: 'event-1',
title: '团队周会',
date: dayjs().format('YYYY-MM-DD'),
startTime: '10:00',
duration: 60,
location: '会议室A',
color: '#645eff',
creator: { id: 'user-1', name: '李齐' },
participants: [
{ id: 'user-1', name: '李齐' },
{ id: 'user-2', name: '张明' },
{ id: 'user-3', name: '王芳' }
],
completed: false
},
{
id: 'event-2',
title: '午餐会议',
date: dayjs().format('YYYY-MM-DD'),
startTime: '12:30',
duration: 90,
location: '公司餐厅',
color: '#0fb8a9',
creator: { id: 'user-2', name: '张明' },
participants: [
{ id: 'user-2', name: '张明' },
{ id: 'user-4', name: '赵琳' }
],
completed: true
}
]);
const commonEvents = ref([
{
id: 'common-1',
title: '健身时间',
duration: 60,
color: '#e85d75'
},
{
id: 'common-2',
title: '学习时间',
duration: 90,
color: '#8c6bed'
},
{
id: 'common-3',
title: '家庭晚餐',
duration: 120,
color: '#f0af56'
}
]);
// 新事件模板
const newEvent = ref({
id: null,
title: '',
date: selectedDate.value,
startTime: '10:00',
duration: 60,
location: '',
color: '#645eff',
creator: null,
participants: [],
completed: false
});
// 计算属性
const displayDate = computed(() => {
if (view.value === 'day') {
return dayjs(currentDate.value).format('YYYY年MM月DD日');
} else if (view.value === 'week') {
const start = dayjs(currentDate.value).startOf('week');
const end = start.add(6, 'day');
return `${start.format('MM月DD日')} - ${end.format('MM月DD日')}`;
} else {
return dayjs(currentDate.value).format('YYYY年MM月');
}
});
// 方法
const changeView = (v) => {
view.value = v;
};
const changeDate = (offset) => {
if (view.value === 'day') {
currentDate.value = dayjs(currentDate.value).add(offset, 'day').format('YYYY-MM-DD');
} else if (view.value === 'week') {
currentDate.value = dayjs(currentDate.value).add(offset, 'week').format('YYYY-MM-DD');
} else {
currentDate.value = dayjs(currentDate.value).add(offset, 'month').format('YYYY-MM-DD');
}
};
const selectDate = (date) => {
selectedDate.value = date;
newEvent.value.date = date;
};
const closeModal = () => {
showEventModal.value = false;
editingEvent.value = null;
newEvent.value = {
id: null,
title: '',
date: selectedDate.value,
startTime: '10:00',
duration: 60,
location: '',
color: '#645eff',
creator: null,
participants: [],
completed: false
};
};
const closeCommonModal = () => {
showCommonEventModal.value = false;
};
const saveEvent = (eventData) => {
if (!eventData.title.trim()) return;
if (!editingEvent.value) {
// 为新事件设置创建者
eventData.creator = {
id: user.value.id,
name: user.value.name
};
// 自动添加创建者为参与者
if (!eventData.participants.some(p => p.id === user.value.id)) {
eventData.participants.push({
id: user.value.id,
name: user.value.name
});
}
eventData.id = 'event-' + Date.now();
events.value.push({ ...eventData });
} else {
const index = events.value.findIndex(e => e.id === editingEvent.value.id);
if (index !== -1) {
events.value[index] = { ...eventData };
}
}
localStorage.setItem('events', JSON.stringify(events.value));
closeModal();
};
const deleteEvent = () => {
if (editingEvent.value) {
events.value = events.value.filter(e => e.id !== editingEvent.value.id);
localStorage.setItem('events', JSON.stringify(events.value));
}
closeModal();
};
const saveCommonEvent = (eventData) => {
if (!eventData.title.trim()) return;
commonEvents.value.push({
...eventData,
id: 'common-' + Date.now()
});
localStorage.setItem('commonEvents', JSON.stringify(commonEvents.value));
closeCommonModal();
};
const showEventDetail = (event) => {
editingEvent.value = event;
showEventModal.value = true;
};
// 处理拖拽开始
const handleDragStart = (event, item, type) => {
event.dataTransfer.setData('type', type);
event.dataTransfer.setData('data', JSON.stringify(item));
event.target.classList.add('dragging');
};
// 处理拖拽结束
const handleDragEnd = (event) => {
document.querySelectorAll('.dragging').forEach(el => {
el.classList.remove('dragging');
});
};
// 处理放置事件
const handleDropEvent = (event, date, time = null) => {
const type = event.dataTransfer.getData('type');
const data = JSON.parse(event.dataTransfer.getData('data'));
if (type === 'common') {
// 添加新日程
const newEvent = {
id: 'event-' + Date.now(),
title: data.title,
date: date,
startTime: time || '09:00',
duration: data.duration,
location: '',
color: data.color,
creator: {
id: user.value.id,
name: user.value.name
},
participants: [{
id: user.value.id,
name: user.value.name
}],
completed: false
};
events.value.push(newEvent);
} else if (type === 'event') {
// 移动现有日程
const eventToUpdate = events.value.find(e => e.id === data.id);
if (eventToUpdate) {
eventToUpdate.date = date;
if (time) {
eventToUpdate.startTime = time;
}
}
}
localStorage.setItem('events', JSON.stringify(events.value));
// 移除拖拽样式
document.querySelectorAll('.dropzone').forEach(el => {
el.classList.remove('drag-over');
});
};
// 处理登录
const handleLogin = (userData) => {
user.value = {
...userData,
team: '萧康云医技术开发团队-医生组'
};
showLoginModal.value = false;
};
// 初始化
onMounted(() => {
const savedEvents = localStorage.getItem('events');
if (savedEvents) {
events.value = JSON.parse(savedEvents);
}
const savedCommonEvents = localStorage.getItem('commonEvents');
if (savedCommonEvents) {
commonEvents.value = JSON.parse(savedCommonEvents);
}
});
2025-04-24 13:39:24 +08:00
</script>
2025-06-10 17:03:02 +08:00
<style>
.dragging {
opacity: 0.7;
transform: scale(0.98);
}
.dropzone.drag-over {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% {
box-shadow: inset 0 0 0 0 rgba(100, 94, 255, 0.4);
}
70% {
box-shadow: inset 0 0 0 10px rgba(100, 94, 255, 0);
}
100% {
box-shadow: inset 0 0 0 0 rgba(100, 94, 255, 0);
}
}
/* 按钮悬停效果 */
button {
transition: all 0.3s ease;
cursor: pointer;
}
2025-04-24 13:39:24 +08:00
2025-06-10 17:03:02 +08:00
button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
2025-05-15 21:24:56 +08:00
2025-06-10 17:03:02 +08:00
button:active {
transform: translateY(1px);
}
</style>