up
This commit is contained in:
@@ -17,7 +17,9 @@
|
|||||||
"file_hash_err":-55,
|
"file_hash_err":-55,
|
||||||
"file_save_err":-56,
|
"file_save_err":-56,
|
||||||
"file_not_found":-57,
|
"file_not_found":-57,
|
||||||
"file_part_err":-58
|
"file_part_err":-58,
|
||||||
|
"schedule_event_not_find":-61,
|
||||||
|
"schedule_permission_denied":-62
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,7 @@ type TabScheduleLog struct {
|
|||||||
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime;comment:操作时间"`
|
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime;comment:操作时间"`
|
||||||
}
|
}
|
||||||
type fromAddEvent struct {
|
type fromAddEvent struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
Title string `json:"title" binding:"required"` // 日程标题
|
Title string `json:"title" binding:"required"` // 日程标题
|
||||||
Start string `json:"start" binding:"required"` // 开始日期
|
Start string `json:"start" binding:"required"` // 开始日期
|
||||||
End string `json:"end" binding:"required"` // 结束日期
|
End string `json:"end" binding:"required"` // 结束日期
|
||||||
@@ -117,9 +118,9 @@ func ApiSchedule(r *gin.RouterGroup) {
|
|||||||
if slices.Contains(scheduleAdmins,user.ID){
|
if slices.Contains(scheduleAdmins,user.ID){
|
||||||
temp["edit"] = true
|
temp["edit"] = true
|
||||||
}
|
}
|
||||||
// if item.UserID == user.ID || item.UserID == 1 {
|
if item.UserID == user.ID {
|
||||||
// temp["edit"] = true
|
temp["edit"] = true
|
||||||
// }
|
}
|
||||||
|
|
||||||
// user_group_find := models.TabUserGroupBinds_{}
|
// user_group_find := models.TabUserGroupBinds_{}
|
||||||
// if models.DB.Where("user_id = ? AND group_id = ?", user.ID, userGroup.ID).First(&user_group_find).Error == nil { //是应用管理员
|
// 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) {
|
r.POST("/addevent", func(ctx *gin.Context) {
|
||||||
isAuth, user, data := AuthenticationAuthority(ctx)
|
isAuth, user, data := AuthenticationAuthority(ctx)
|
||||||
if isAuth {
|
if isAuth {
|
||||||
|
|||||||
@@ -10,4 +10,8 @@ export const scheduleApi = {
|
|||||||
addEvent(data) {
|
addEvent(data) {
|
||||||
return api.post('/schedule/addevent', data)
|
return api.post('/schedule/addevent', data)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
editEvent(data) {
|
||||||
|
return api.post('/schedule/editevent', data)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
// Vue 核心响应式 API
|
// Vue 核心响应式 API
|
||||||
import { ref, watch, onMounted, onBeforeUnmount } from "vue";
|
import { ref, watch, onMounted, onBeforeUnmount,reactive } from "vue";
|
||||||
// FullCalendar Vue 3 组件
|
// FullCalendar Vue 3 组件
|
||||||
import FullCalendar from "@fullcalendar/vue3";
|
import FullCalendar from "@fullcalendar/vue3";
|
||||||
// FullCalendar 插件:月视图
|
// FullCalendar 插件:月视图
|
||||||
@@ -49,14 +49,8 @@ const calendarRef = ref(null);
|
|||||||
// 当前视图的年份
|
// 当前视图的年份
|
||||||
const calendarNowShow = ref();
|
const calendarNowShow = ref();
|
||||||
|
|
||||||
// 用于跟踪上次点击时间的响应式变量
|
|
||||||
const lastClickTime = ref(0);
|
|
||||||
// 用于跟踪上次点击event时间的响应式变量
|
|
||||||
const lastEventClickTime = ref(0);
|
|
||||||
|
|
||||||
// 模态框相关状态
|
// 模态框相关状态
|
||||||
const showModal = ref(false);
|
const showModal = ref(false);
|
||||||
const modalTitle = ref("添加日程");
|
|
||||||
const eventData = ref({
|
const eventData = ref({
|
||||||
title: "",
|
title: "",
|
||||||
startDate: "",
|
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({
|
const calendarOptions = ref({
|
||||||
// 日历高度:占满可用空间
|
// 日历高度:占满可用空间
|
||||||
@@ -202,21 +241,21 @@ const calendarOptions = ref({
|
|||||||
// 日期点击事件处理函数
|
// 日期点击事件处理函数
|
||||||
dateClick(info) {
|
dateClick(info) {
|
||||||
const nowTime = new Date().getTime();
|
const nowTime = new Date().getTime();
|
||||||
const timeDifference = nowTime - lastClickTime.value;
|
const timeDifference = nowTime - pageData.lastClickTime;
|
||||||
|
|
||||||
// 判断是否为双击(400ms 内连续点击)
|
//判断和上次点击的是不是同一天
|
||||||
if (timeDifference < 400 && timeDifference > 0) {
|
if(info.dateStr===pageData.lastClickTimeStr){
|
||||||
console.log("双击日期:", info.dateStr);
|
// 判断是否为双击(400ms 内连续点击)
|
||||||
// 双击功能:快速添加事件
|
if (timeDifference < 400 && timeDifference > 0) {
|
||||||
handleDoubleClick(info);
|
// 双击功能:快速添加事件
|
||||||
} else {
|
handleDoubleClick(info);
|
||||||
console.log("单击日期:", info.dateStr);
|
}
|
||||||
// 单击功能:
|
|
||||||
handleSingleClick(info);
|
|
||||||
}
|
}
|
||||||
|
pageData.lastClickTimeStr=info.dateStr;
|
||||||
|
|
||||||
|
|
||||||
// 更新上次点击时间
|
// 更新上次点击时间
|
||||||
lastClickTime.value = nowTime;
|
pageData.lastClickTime = nowTime;
|
||||||
},
|
},
|
||||||
|
|
||||||
//选择日期
|
//选择日期
|
||||||
@@ -234,22 +273,57 @@ const calendarOptions = ref({
|
|||||||
//事件event点击处理函数
|
//事件event点击处理函数
|
||||||
eventClick(info) {
|
eventClick(info) {
|
||||||
const nowTime = new Date().getTime();
|
const nowTime = new Date().getTime();
|
||||||
const timeDifference = nowTime - lastEventClickTime.value;
|
const timeDifference = nowTime - pageData.lastEventClickTime;
|
||||||
|
|
||||||
// 判断是否为双击(400ms 内连续点击)
|
// 单击功能:
|
||||||
if (timeDifference < 400 && timeDifference > 0) {
|
var eventid=parseInt(info.event.id)
|
||||||
console.log("双击事件:", info);
|
seleEvent(eventid);
|
||||||
// 双击功能:快速添加事件
|
|
||||||
} else {
|
//判断和上次点击的是不是同一个event
|
||||||
console.log("单击事件:", info);
|
if(eventid===pageData.lastEventClickID){
|
||||||
// 单击功能:显示日期详情
|
// 判断是否为双击(400ms 内连续点击)
|
||||||
|
if (timeDifference < 400 && timeDifference > 0) {
|
||||||
|
//console.log("双击事件:", info);
|
||||||
|
// 双击功能:
|
||||||
|
unseleEventAll()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
pageData.lastEventClickID=eventid;
|
||||||
|
|
||||||
|
|
||||||
// 更新上次点击时间
|
// 更新上次点击时间
|
||||||
lastEventClickTime.value = nowTime;
|
pageData.lastEventClickTime = nowTime;
|
||||||
},
|
},
|
||||||
|
|
||||||
//event拖动处理
|
//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 = () => {
|
const saveEvent = () => {
|
||||||
if (!eventData.value.title.trim()) {
|
if (!eventData.value.title.trim()) {
|
||||||
//alert("请输入日程内容");
|
//alert("请输入日程内容");
|
||||||
|
pageData.submitChecked=true;
|
||||||
toast.warning(t("schedule.event_title_required"));
|
toast.warning(t("schedule.event_title_required"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
pageData.submitChecked=false;
|
||||||
|
|
||||||
if (!eventData.value.startDate || !eventData.value.endDate) {
|
if (!eventData.value.startDate || !eventData.value.endDate) {
|
||||||
//alert("请选择日期");
|
//alert("请选择日期");
|
||||||
@@ -331,11 +394,13 @@ const saveEvent = () => {
|
|||||||
// 添加到日历事件列表
|
// 添加到日历事件列表
|
||||||
//提交到后端
|
//提交到后端
|
||||||
|
|
||||||
|
//console.log(newEvent)
|
||||||
|
|
||||||
scheduleApi
|
scheduleApi
|
||||||
.addEvent({
|
.addEvent({
|
||||||
title: newEvent.title,
|
title: newEvent.title,
|
||||||
start: newEvent.start,
|
start: newEvent.start,
|
||||||
end: DateUtils.toRealEnd(newEvent.end),
|
end: newEvent.end===newEvent.start?newEvent.end:DateUtils.toRealEnd(newEvent.end),
|
||||||
color: newEvent.backgroundColor,
|
color: newEvent.backgroundColor,
|
||||||
})
|
})
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
@@ -369,7 +434,7 @@ const getEvents = () => {
|
|||||||
end: DateUtils.toRealEnd(calendarNowShow.value.end),
|
end: DateUtils.toRealEnd(calendarNowShow.value.end),
|
||||||
})
|
})
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
console.log(r);
|
//console.log(r);
|
||||||
if (r.errCode == 0) {
|
if (r.errCode == 0) {
|
||||||
//前端提交是否错误
|
//前端提交是否错误
|
||||||
switch (
|
switch (
|
||||||
@@ -378,15 +443,15 @@ const getEvents = () => {
|
|||||||
case 0:
|
case 0:
|
||||||
calendarOptions.value.events=[];
|
calendarOptions.value.events=[];
|
||||||
var events = r.raw.return.list;
|
var events = r.raw.return.list;
|
||||||
console.log(events);
|
//console.log(events);
|
||||||
var eventstemp = [];
|
var eventstemp = [];
|
||||||
events.forEach((item) => {
|
events?.forEach((item) => {
|
||||||
|
|
||||||
calendarOptions.value.events.push({
|
calendarOptions.value.events.push({
|
||||||
id: item.ID, // 后端 ID
|
id: item.ID, // 后端 ID
|
||||||
title: item.Title, // 标题
|
title: item.Title, // 标题
|
||||||
start: item.StartDate, // 开始日期
|
start: item.StartDate, // 开始日期
|
||||||
end: DateUtils.toCalendarEnd(item.EndDate), // 结束日期
|
end: item.StartDate===item.EndDate?item.EndDate:DateUtils.toCalendarEnd(item.EndDate), // 结束日期
|
||||||
backgroundColor: item.BgColor, // 背景色
|
backgroundColor: item.BgColor, // 背景色
|
||||||
borderColor: item.BgColor, // 边框色(一般和背景一样)
|
borderColor: item.BgColor, // 边框色(一般和背景一样)
|
||||||
allDay: true, // 全天事件
|
allDay: true, // 全天事件
|
||||||
@@ -531,6 +596,10 @@ onMounted(() => {
|
|||||||
<div class="uni-easyinput input relative">
|
<div class="uni-easyinput input relative">
|
||||||
<div
|
<div
|
||||||
class="uni-easyinput__content is-input-border border border-gray-300 rounded-md bg-white relative"
|
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
|
<input
|
||||||
v-model="eventData.title"
|
v-model="eventData.title"
|
||||||
|
|||||||
Reference in New Issue
Block a user