前端功能基本完成

This commit is contained in:
2026-04-02 16:57:24 +08:00
parent 251cd64e23
commit 5e2b326838
5 changed files with 176 additions and 68 deletions
@@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, watch, defineProps,onMounted } from "vue"; import { ref, watch, defineProps, onMounted } from "vue";
// FullCalendar Vue 3 组件 // FullCalendar Vue 3 组件
import FullCalendar from "@fullcalendar/vue3"; import FullCalendar from "@fullcalendar/vue3";
@@ -8,17 +8,22 @@ import dayGridPlugin from "@fullcalendar/daygrid";
// FullCalendar 插件:交互功能(拖拽、点击等) // FullCalendar 插件:交互功能(拖拽、点击等)
import interactionPlugin from "@fullcalendar/interaction"; import interactionPlugin from "@fullcalendar/interaction";
import { useDateUtils } from "@/composables/useDateUtils";
const DateUtils = useDateUtils();
// FullCalendar 组件的引用,用于调用日历 API // FullCalendar 组件的引用,用于调用日历 API
const calendarRef = ref(null); const calendarRef = ref(null);
// 用于跟踪上次点击event时间的响应式变量 // 用于跟踪上次点击event时间的响应式变量
const lastEventClickTime = ref(0); const lastEventClickTime = ref(0);
var firstClickOnDate=false; var firstClickOnDate = false;
var dataStartTemp="" var dataStartTemp = "";
// 国际化 hook // 国际化 hook
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import Component from "vue-flatpickr-component";
// 获取国际化翻译函数和当前语言 // 获取国际化翻译函数和当前语言
const { t, locale } = useI18n(); const { t, locale } = useI18n();
@@ -46,7 +51,6 @@ const props = defineProps({
required: false, required: false,
default: "", default: "",
}, },
}); });
const eventData = ref({ const eventData = ref({
@@ -60,23 +64,41 @@ const eventData = ref({
}); });
function passing_date_characters(startDate, endDate) { function passing_date_characters(startDate, endDate) {
//需要先判断大小确定哪个是开始日期哪个是结束日期
var tempStart = DateUtils.strToDate(startDate);
var tempEnd = DateUtils.strToDate(endDate);
if (tempStart > tempEnd) {
//反了
eventData.value.start = endDate;
//end 需要加一天
eventData.value.end = DateUtils.dateToStr(
DateUtils.toCalendarEnd(startDate),
);
} else {
eventData.value.start = startDate;
//end 需要加一天
eventData.value.end = DateUtils.dateToStr(DateUtils.toCalendarEnd(endDate));
}
}
function passing_date_characters_Select(startDate, endDate) {
//滑动选择日期的参数来自FullCalendar,不需要加一天也不需要判断大小,而且直接就是字符串类型
eventData.value.start = startDate; eventData.value.start = startDate;
eventData.value.end = endDate; eventData.value.end = endDate;
} }
// 监听props变化,更新本地eventData // 监听props变化,更新本地eventData
watch( watch(
() => props.startDate, () => props.start,
(newVal) => { (newVal) => {
eventData.value.startDate = newVal; eventData.value.start = newVal;
}, },
); );
watch( watch(
() => props.endDate, () => props.end,
(newVal) => { (newVal) => {
eventData.value.endDate = newVal; eventData.value.end = newVal;
}, },
); );
@@ -94,7 +116,6 @@ watch(
}, },
); );
// 定义事件发射:通知父组件日期变化 // 定义事件发射:通知父组件日期变化
const emit = defineEmits(["update:startDate", "update:endDate", "clearDates"]); const emit = defineEmits(["update:startDate", "update:endDate", "clearDates"]);
@@ -110,21 +131,19 @@ function clearDates() {
// 监听本地eventData变化,同步更新到父组件 // 监听本地eventData变化,同步更新到父组件
watch( watch(
() => eventData.value.startDate, () => eventData.value.start,
(newVal) => { (newVal) => {
emit("update:startDate", newVal); emit("update:startDate", newVal);
}, },
); );
watch( watch(
() => eventData.value.endDate, () => eventData.value.end,
(newVal) => { (newVal) => {
emit("update:endDate", newVal); emit("update:endDate", newVal);
}, },
); );
// 日历配置选项 // 日历配置选项
const calendarOptions = ref({ const calendarOptions = ref({
// 日历高度:占满可用空间 // 日历高度:占满可用空间
@@ -229,71 +248,74 @@ const calendarOptions = ref({
// 日期点击事件处理函数 // 日期点击事件处理函数
dateClick(info) { dateClick(info) {
console.log(info); //console.log(info);
if(firstClickOnDate)
{
firstClickOnDate=false;
passing_date_characters(dataStartTemp,info.dateStr);
}else{
firstClickOnDate=true;
dataStartTemp=info.dateStr;
}
if (firstClickOnDate) {
firstClickOnDate = false;
passing_date_characters(dataStartTemp, info.dateStr);
} else {
firstClickOnDate = true;
dataStartTemp = info.dateStr;
}
}, },
//选择日期 //选择日期
select(info) { select(info) {
if (info.end - info.start > 86400000) { if (info.end - info.start > 86400000) {
//选择了多日 //选择了多日
console.log("选择了多日:", info); //console.log("选择了多日:", info);
passing_date_characters_Select(info.startStr, info.endStr);
} else { } else {
//选择单日 //选择单日
console.log("选择单日:", info); //console.log("选择单日:", info);
} }
}, },
//事件event点击处理函数 //事件event点击处理函数
eventClick(info) { // eventClick(info) {
const nowTime = new Date().getTime(); // const nowTime = new Date().getTime();
const timeDifference = nowTime - lastEventClickTime.value; // const timeDifference = nowTime - lastEventClickTime.value;
// 判断是否为双击(400ms 内连续点击) // // 判断是否为双击(400ms 内连续点击)
if (timeDifference < 400 && timeDifference > 0) { // if (timeDifference < 400 && timeDifference > 0) {
console.log("双击事件:", info); // console.log("双击事件:", info);
// 双击功能:快速添加事件 // // 双击功能:快速添加事件
} else { // } else {
console.log("单击事件:", info); // console.log("单击事件:", info);
// 单击功能:显示日期详情 // // 单击功能:显示日期详情
} // }
// 更新上次点击时间 // // 更新上次点击时间
lastEventClickTime.value = nowTime; // lastEventClickTime.value = nowTime;
}, // },
//event拖动处理 //event拖动处理
eventDrop(info) {}, eventDrop(info) {
//console.log(info);
passing_date_characters_Select(info.event.startStr, info.event.endStr);
},
}); });
function switchShow(){ function switchShow() {
if(isShow.value) if (isShow.value) {
{ isShow.value = false;
isShow.value=false; } else {
}else{ isShow.value = true;
isShow.value=true;
} }
} }
onMounted(()=>{ function splicingDataWeek(data) {
return data + " " + DateUtils.getI18nWeekday(data);
}
onMounted(() => {
calendarOptions.value.events.push(eventData.value); calendarOptions.value.events.push(eventData.value);
}); });
</script> </script>
<template> <template>
<div class="mb-4"> <div class="mb-4">
<div <div
@click="switchShow" @click="switchShow"
class="flex items-center gap-2 border border-gray-200 rounded-xl px-3 py-1.5 shadow-sm bg-white mb-4" class="flex items-center gap-2 border border-gray-200 rounded-xl px-3 py-1.5 shadow-sm bg-white mb-4 sticky top-0 z-50"
> >
<!-- 日历图标 --> <!-- 日历图标 -->
<div class="date-icon"> <div class="date-icon">
@@ -329,19 +351,21 @@ onMounted(()=>{
<!-- 日期显示 --> <!-- 日期显示 -->
<div class="date-display flex items-center justify-between gap-2 flex-1"> <div class="date-display flex items-center justify-between gap-2 flex-1">
<div class="start-date text-gray-700 font-medium"> <div class="start-date text-gray-700 font-medium">
{{ eventData.start }} {{ splicingDataWeek(eventData.start) }}
</div> </div>
<div class="text-gray-500">{{ t("schedule.to") }}</div> <div class="text-gray-500">{{ t("schedule.to") }}</div>
<div class="end-date text-gray-700 font-medium"> <div class="end-date text-gray-700 font-medium">
{{ eventData.end }} {{
splicingDataWeek(
eventData.start === eventData.end
? eventData.end
: DateUtils.toRealEnd(eventData.end),
)
}}
</div> </div>
</div> </div>
</div> </div>
<FullCalendar <FullCalendar v-if="isShow" ref="calendarRef" :options="calendarOptions" />
v-if="isShow"
ref="calendarRef"
:options="calendarOptions"
/>
</div> </div>
</template> </template>
@@ -0,0 +1,65 @@
// composables/useDateUtils.js
import { useI18n } from "vue-i18n";
/**
* 日期字符串 转 日期对象 (纯日期,无时间)
* @param {string} dateStr - 格式:YYYY-MM-DD 例如 "2026-04-02"
* @returns {Date} 日期对象(时间自动设为 00:00:00)
*/
export function strToDate(dateStr) {
return new Date(dateStr);
}
/**
* 日期对象 转回 日期字符串 (YYYY-MM-DD)
* @param {Date} date - 日期对象
* @returns {string} 格式:YYYY-MM-DD
*/
export function dateToStr(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
// 封装成组合式函数,方便在组件中按需引入
export const useDateUtils = () => {
const { t } = useI18n();
return {
strToDate,
dateToStr,
// 可以直接把 FullCalendar 常用的日期处理也封装进来
/**
* FullCalendar 显示用:给结束日期 +1 天(包含当天)
* @param {string | Date} endDate - 真实结束日期
* @returns {Date} FullCalendar 可用的 end 日期
*/
toCalendarEnd: (endDate) => {
const date =
typeof endDate === "string" ? strToDate(endDate) : new Date(endDate);
date.setDate(date.getDate() + 1);
return date;
},
/**
* 提交数据库用:给 FullCalendar end 日期 -1 天(还原真实结束日)
* @param {Date} calendarEnd - FullCalendar 返回的 end 日期
* @returns {string} 可存库的 YYYY-MM-DD 字符串
*/
toRealEnd: (calendarEnd) => {
const date = new Date(calendarEnd);
date.setDate(date.getDate() - 1);
return dateToStr(date);
},
/**
* 获取日期是星期几
* @param {string | Date} date - YYYY-MM-DD 或 Date 对象
* @returns {string} 星期一、星期二...星期日
*/
getI18nWeekday(date) {
const d = typeof date === "string" ? strToDate(date) : new Date(date);
const weekKeys = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
const key = weekKeys[d.getDay()];
return t(`week.${key}`); // 自动切换中英文
},
};
};
+9
View File
@@ -1,4 +1,13 @@
{ {
"week": {
"sun": "Sunday",
"mon": "Monday",
"tue": "Tuesday",
"wed": "Wednesday",
"thu": "Thursday",
"fri": "Friday",
"sat": "Saturday"
},
"errorpage": { "errorpage": {
"404_title": "404 Resource Not Found", "404_title": "404 Resource Not Found",
"404_msg_title": "Oops… You just found an error page", "404_msg_title": "Oops… You just found an error page",
+9
View File
@@ -1,4 +1,13 @@
{ {
"week": {
"sun": "星期日",
"mon": "星期一",
"tue": "星期二",
"wed": "星期三",
"thu": "星期四",
"fri": "星期五",
"sat": "星期六"
},
"errorpage": { "errorpage": {
"404_title": "404 资源未找到", "404_title": "404 资源未找到",
"404_msg_title": "抱歉…您刚刚发现了一个错误页面", "404_msg_title": "抱歉…您刚刚发现了一个错误页面",
@@ -181,7 +181,7 @@ const calendarOptions = ref({
handleDoubleClick(info); handleDoubleClick(info);
} else { } else {
console.log("单击日期:", info.dateStr); console.log("单击日期:", info.dateStr);
// 单击功能:显示日期详情 // 单击功能:
handleSingleClick(info); handleSingleClick(info);
} }
@@ -194,9 +194,10 @@ const calendarOptions = ref({
if (info.end - info.start > 86400000) { if (info.end - info.start > 86400000) {
//选择了多日 //选择了多日
console.log("选择了多日:", info); console.log("选择了多日:", info);
openEventModal(info.startStr,info.endStr);
} else { } else {
//选择单日 //选择单日 无功能
console.log("选择单日:", info); //console.log("选择单日:", info);
} }
}, },
@@ -222,11 +223,11 @@ const calendarOptions = ref({
}); });
// 打开模态框 // 打开模态框
const openEventModal = (dateStr) => { const openEventModal = (dateStr,dataEnd) => {
eventData.value = { eventData.value = {
title: "", title: "",
startDate: dateStr, startDate: dateStr,
endDate: dateStr, endDate: dataEnd,
color: "#066FD1", color: "#066FD1",
}; };
@@ -240,7 +241,7 @@ const closeEventModal = () => {
// 处理双击事件:打开模态框添加事件 // 处理双击事件:打开模态框添加事件
const handleDoubleClick = (info) => { const handleDoubleClick = (info) => {
openEventModal(info.dateStr); openEventModal(info.dateStr,info.dateStr);
}; };
// 处理单机事件:显示日期详情 // 处理单机事件:显示日期详情
@@ -420,8 +421,8 @@ onMounted(() => {
<div class="modal-body p-4 flex-1 overflow-y-auto"> <div class="modal-body p-4 flex-1 overflow-y-auto">
<!-- 日期选择区域 --> <!-- 日期选择区域 -->
<DatatimePickerForFullCalendar <DatatimePickerForFullCalendar
:start-date="eventData.startDate" v-model:startDate="eventData.startDate"
:end-date="eventData.endDate" v-model:endDate="eventData.endDate"
:color="eventData.color" :color="eventData.color"
:title="eventData.title" :title="eventData.title"
/> />