This commit is contained in:
2026-04-04 01:53:48 +08:00
parent ca4455517c
commit 8bb3fa7507
4 changed files with 200 additions and 50 deletions
+3 -1
View File
@@ -17,7 +17,9 @@
"file_hash_err":-55,
"file_save_err":-56,
"file_not_found":-57,
"file_part_err":-58
"file_part_err":-58,
"schedule_event_not_find":-61,
"schedule_permission_denied":-62
}
+78 -3
View File
@@ -40,6 +40,7 @@ type TabScheduleLog struct {
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime;comment:操作时间"`
}
type fromAddEvent struct {
ID uint `json:"id"`
Title string `json:"title" binding:"required"` // 日程标题
Start string `json:"start" binding:"required"` // 开始日期
End string `json:"end" binding:"required"` // 结束日期
@@ -117,9 +118,9 @@ func ApiSchedule(r *gin.RouterGroup) {
if slices.Contains(scheduleAdmins,user.ID){
temp["edit"] = true
}
// if item.UserID == user.ID || item.UserID == 1 {
// temp["edit"] = true
// }
if item.UserID == user.ID {
temp["edit"] = true
}
// user_group_find := models.TabUserGroupBinds_{}
// if models.DB.Where("user_id = ? AND group_id = ?", user.ID, userGroup.ID).First(&user_group_find).Error == nil { //是应用管理员
@@ -142,6 +143,80 @@ func ApiSchedule(r *gin.RouterGroup) {
})
r.POST("/editevent", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if isAuth {
var from fromAddEvent
if err := mapstructure.Decode(data, &from); err == nil {
//先从数据库拉取原始event数据
oldEvent:=TabSchedule{
ID: from.ID,
}
if models.DB.Where(&oldEvent).First(&oldEvent).Error==nil{
//需要先判断修改权限
var isCanEdit=false
if slices.Contains(scheduleAdmins,user.ID){ //用户id是管理员
isCanEdit = true
}
if oldEvent.UserID==user.ID{//event是用户创建的
isCanEdit = true
}
if isCanEdit{
tosql := TabSchedule{
// UserID: user.ID, //如果是管理员修改的话会覆盖掉创建者的id
Title: from.Title,
StartDate: from.Start,
EndDate: from.End,
BgColor: from.Color,
}
//fmt.Println(tosql)
findEvent:=TabSchedule{
ID: oldEvent.ID,
}
if models.DB.Where(&findEvent).Updates(&tosql).Error==nil{
//应该修改完了 写日志
//把最新数据再读出来
models.DB.Where(&findEvent).First(&findEvent)
newContent, _ := json.Marshal(findEvent) //转 JSON
oldContent, _ := json.Marshal(oldEvent) //转 JSON
tosqllog := TabScheduleLog{
UserID: user.ID,
ScheduleID: oldEvent.ID,
ActionType: "update",
NewContent: string(newContent),
OldContent: string(oldContent),
IP: ctx.ClientIP(),
}
models.DB.Create(&tosqllog)
ReturnJson(ctx, "apiOK", nil)
}else{
ReturnJson(ctx, "apiErr", nil)
}
}else{
ReturnJson(ctx, "schedule_permission_denied", nil)
}
}else{
ReturnJson(ctx, "schedule_event_not_find", nil)
}
} else {
ReturnJson(ctx, "jsonErr", nil)
}
} else {
ReturnJson(ctx, "userCookieError", nil)
}
})
r.POST("/addevent", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if isAuth {
+4
View File
@@ -10,4 +10,8 @@ export const scheduleApi = {
addEvent(data) {
return api.post('/schedule/addevent', data)
},
editEvent(data) {
return api.post('/schedule/editevent', data)
},
}
+109 -40
View File
@@ -1,6 +1,6 @@
<script setup>
// Vue 核心响应式 API
import { ref, watch, onMounted, onBeforeUnmount } from "vue";
import { ref, watch, onMounted, onBeforeUnmount,reactive } from "vue";
// FullCalendar Vue 3 组件
import FullCalendar from "@fullcalendar/vue3";
// FullCalendar 插件:月视图
@@ -49,14 +49,8 @@ const calendarRef = ref(null);
// 当前视图的年份
const calendarNowShow = ref();
// 用于跟踪上次点击时间的响应式变量
const lastClickTime = ref(0);
// 用于跟踪上次点击event时间的响应式变量
const lastEventClickTime = ref(0);
// 模态框相关状态
const showModal = ref(false);
const modalTitle = ref("添加日程");
const eventData = ref({
title: "",
startDate: "",
@@ -86,6 +80,51 @@ const colorOptions = ref([
},
]);
const pageData =reactive({//本页全局变量
seleEventID:0,//上次点击的eventid
lastClickTime:0,// 用于跟踪上次点击时间的响应式变量
lastClickTimeStr:"",
lastEventClickTime:0,// 用于跟踪上次点击event时间的响应式变量
lastEventClickID:0,
submitChecked:false,
})
function unseleEvent(eventID)
{
//寻找哪个event被单击了并修改边框
const target = calendarOptions.value.events.find(item => item.id === eventID)
if(target){
target.borderColor=target.backgroundColor;
}
}
function unseleEventAll()
{
unseleEvent(pageData.seleEventID)
pageData.seleEventID=0;
}
function seleEvent(eventID)
{
//单击了event
//取消上次选中
if(pageData.seleEventID!=0)
{
unseleEvent(pageData.seleEventID);
}
//寻找哪个event被单击了并修改边框
const target = calendarOptions.value.events.find(item => item.id === eventID)
if(target){
target.borderColor="#000000";
}
pageData.seleEventID=eventID;
}
// 日历配置选项
const calendarOptions = ref({
// 日历高度:占满可用空间
@@ -202,21 +241,21 @@ const calendarOptions = ref({
// 日期点击事件处理函数
dateClick(info) {
const nowTime = new Date().getTime();
const timeDifference = nowTime - lastClickTime.value;
const timeDifference = nowTime - pageData.lastClickTime;
//判断和上次点击的是不是同一天
if(info.dateStr===pageData.lastClickTimeStr){
// 判断是否为双击(400ms 内连续点击)
if (timeDifference < 400 && timeDifference > 0) {
console.log("双击日期:", info.dateStr);
// 双击功能:快速添加事件
handleDoubleClick(info);
} else {
console.log("单击日期:", info.dateStr);
// 单击功能:
handleSingleClick(info);
}
}
pageData.lastClickTimeStr=info.dateStr;
// 更新上次点击时间
lastClickTime.value = nowTime;
pageData.lastClickTime = nowTime;
},
//选择日期
@@ -234,22 +273,57 @@ const calendarOptions = ref({
//事件event点击处理函数
eventClick(info) {
const nowTime = new Date().getTime();
const timeDifference = nowTime - lastEventClickTime.value;
const timeDifference = nowTime - pageData.lastEventClickTime;
// 单击功能:
var eventid=parseInt(info.event.id)
seleEvent(eventid);
//判断和上次点击的是不是同一个event
if(eventid===pageData.lastEventClickID){
// 判断是否为双击(400ms 内连续点击)
if (timeDifference < 400 && timeDifference > 0) {
console.log("双击事件:", info);
// 双击功能:快速添加事件
} else {
console.log("单击事件:", info);
// 单击功能:显示日期详情
//console.log("双击事件:", info);
// 双击功能:
unseleEventAll()
}
}
pageData.lastEventClickID=eventid;
// 更新上次点击时间
lastEventClickTime.value = nowTime;
pageData.lastEventClickTime = nowTime;
},
//event拖动处理
eventDrop(info) {},
eventDrop(info) {
//需要发送到后端修改event
//console.log(info.event)
scheduleApi
.editEvent({
id:parseInt(info.event.id),
title: info.event.title,
start: info.event.startStr,
end: info.event.end===null?info.event.startStr:DateUtils.toRealEnd(info.event.end),
color: info.event.backgroundColor,
})
.then((r) => {
//console.log(r);
if (r.errCode == 0) {
//前端提交是否错误
switch (
r.raw.err_code //后端返回是否错误
) {
case 0:
getEvents();//从新从后端获取最新数据
break;
default:
toast.danger(t("message.server_error"));
break;
}
}
});
},
});
// 打开模态框
@@ -280,28 +354,17 @@ const handleDoubleClick = (info) => {
}
};
// 处理单机事件:显示日期详情
const handleSingleClick = (info) => {
const dateEvents = calendarOptions.value.events.filter(
(event) =>
event.start === info.dateStr ||
(event.start <= info.dateStr && event.end > info.dateStr),
);
if (dateEvents.length > 0) {
//alert(`${info.dateStr} 有 ${dateEvents.length} 个事件`)
} else {
//alert(`${info.dateStr} 没有事件`)
}
};
// 保存日程事件
const saveEvent = () => {
if (!eventData.value.title.trim()) {
//alert("请输入日程内容");
pageData.submitChecked=true;
toast.warning(t("schedule.event_title_required"));
return;
}
pageData.submitChecked=false;
if (!eventData.value.startDate || !eventData.value.endDate) {
//alert("请选择日期");
@@ -331,11 +394,13 @@ const saveEvent = () => {
// 添加到日历事件列表
//提交到后端
//console.log(newEvent)
scheduleApi
.addEvent({
title: newEvent.title,
start: newEvent.start,
end: DateUtils.toRealEnd(newEvent.end),
end: newEvent.end===newEvent.start?newEvent.end:DateUtils.toRealEnd(newEvent.end),
color: newEvent.backgroundColor,
})
.then((r) => {
@@ -369,7 +434,7 @@ const getEvents = () => {
end: DateUtils.toRealEnd(calendarNowShow.value.end),
})
.then((r) => {
console.log(r);
//console.log(r);
if (r.errCode == 0) {
//前端提交是否错误
switch (
@@ -378,15 +443,15 @@ const getEvents = () => {
case 0:
calendarOptions.value.events=[];
var events = r.raw.return.list;
console.log(events);
//console.log(events);
var eventstemp = [];
events.forEach((item) => {
events?.forEach((item) => {
calendarOptions.value.events.push({
id: item.ID, // 后端 ID
title: item.Title, // 标题
start: item.StartDate, // 开始日期
end: DateUtils.toCalendarEnd(item.EndDate), // 结束日期
end: item.StartDate===item.EndDate?item.EndDate:DateUtils.toCalendarEnd(item.EndDate), // 结束日期
backgroundColor: item.BgColor, // 背景色
borderColor: item.BgColor, // 边框色(一般和背景一样)
allDay: true, // 全天事件
@@ -531,6 +596,10 @@ onMounted(() => {
<div class="uni-easyinput input relative">
<div
class="uni-easyinput__content is-input-border border border-gray-300 rounded-md bg-white relative"
:class="{
'border-gray-300': eventData.title ||!pageData.submitChecked,
'border-red-500': !eventData.title && pageData.submitChecked
}"
>
<input
v-model="eventData.title"