前端功能基本完成
This commit is contained in:
@@ -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,63 +248,66 @@ const calendarOptions = ref({
|
|||||||
|
|
||||||
// 日期点击事件处理函数
|
// 日期点击事件处理函数
|
||||||
dateClick(info) {
|
dateClick(info) {
|
||||||
console.log(info);
|
//console.log(info);
|
||||||
|
|
||||||
if(firstClickOnDate)
|
if (firstClickOnDate) {
|
||||||
{
|
firstClickOnDate = false;
|
||||||
firstClickOnDate=false;
|
passing_date_characters(dataStartTemp, info.dateStr);
|
||||||
passing_date_characters(dataStartTemp,info.dateStr);
|
} else {
|
||||||
}else{
|
firstClickOnDate = true;
|
||||||
firstClickOnDate=true;
|
dataStartTemp = info.dateStr;
|
||||||
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>
|
||||||
@@ -293,7 +315,7 @@ onMounted(()=>{
|
|||||||
<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}`); // 自动切换中英文
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -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",
|
||||||
|
|||||||
@@ -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"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user