econtrol-tools-calendar 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1599 @@
1
+ import { defineComponent, ref, computed, watch, reactive, resolveComponent, createElementBlock, openBlock, createElementVNode, createVNode, createCommentVNode, toDisplayString, createBlock, withCtx, createTextVNode, unref, nextTick } from "vue";
2
+ import FullCalendar from "@fullcalendar/vue3";
3
+ import dayGridPlugin from "@fullcalendar/daygrid";
4
+ import timeGridPlugin from "@fullcalendar/timegrid";
5
+ import listPlugin from "@fullcalendar/list";
6
+ import interactionPlugin from "@fullcalendar/interaction";
7
+ import { ElMessage } from "element-plus";
8
+ import { User, Clock, Calendar, Monitor, Document } from "@element-plus/icons-vue";
9
+ var l77 = {
10
+ code: "zh-cn",
11
+ week: {
12
+ // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
13
+ dow: 1,
14
+ doy: 4
15
+ // The week that contains Jan 4th is the first week of the year.
16
+ },
17
+ buttonText: {
18
+ prev: "上月",
19
+ next: "下月",
20
+ today: "今天",
21
+ year: "年",
22
+ month: "月",
23
+ week: "周",
24
+ day: "日",
25
+ list: "日程"
26
+ },
27
+ weekText: "周",
28
+ allDayText: "全天",
29
+ moreLinkText(n) {
30
+ return "另外 " + n + " 个";
31
+ },
32
+ noEventsText: "没有事件显示"
33
+ };
34
+ const _hoisted_1 = { class: "fullcalendar-demo" };
35
+ const _hoisted_2 = { class: "demo-header" };
36
+ const _hoisted_3 = {
37
+ key: 0,
38
+ class: "device-info"
39
+ };
40
+ const _hoisted_4 = { class: "event-detail" };
41
+ const _hoisted_5 = { class: "detail-header" };
42
+ const _hoisted_6 = { class: "detail-title" };
43
+ const _hoisted_7 = { class: "detail-body" };
44
+ const _hoisted_8 = { class: "detail-item" };
45
+ const _hoisted_9 = { class: "detail-label" };
46
+ const _hoisted_10 = { class: "detail-value" };
47
+ const _hoisted_11 = { class: "detail-item" };
48
+ const _hoisted_12 = { class: "detail-label" };
49
+ const _hoisted_13 = { class: "detail-value" };
50
+ const _hoisted_14 = {
51
+ key: 0,
52
+ class: "detail-item"
53
+ };
54
+ const _hoisted_15 = { class: "detail-label" };
55
+ const _hoisted_16 = { class: "detail-value" };
56
+ const _hoisted_17 = {
57
+ key: 1,
58
+ class: "detail-item"
59
+ };
60
+ const _hoisted_18 = { class: "detail-label" };
61
+ const _hoisted_19 = { class: "detail-value" };
62
+ const _hoisted_20 = {
63
+ key: 2,
64
+ class: "detail-item"
65
+ };
66
+ const _hoisted_21 = { class: "detail-label" };
67
+ const _hoisted_22 = { class: "detail-value" };
68
+ const _hoisted_23 = {
69
+ key: 3,
70
+ class: "detail-item"
71
+ };
72
+ const _hoisted_24 = { class: "detail-label" };
73
+ const _hoisted_25 = { class: "detail-value description-text" };
74
+ const _hoisted_26 = { class: "event-content" };
75
+ const _hoisted_27 = { class: "event-time" };
76
+ const _hoisted_28 = { class: "event-title" };
77
+ const _hoisted_29 = {
78
+ key: 0,
79
+ class: "device-detail-content"
80
+ };
81
+ const _hoisted_30 = {
82
+ key: 1,
83
+ class: "device-detail-loading"
84
+ };
85
+ const _sfc_main = /* @__PURE__ */ defineComponent({
86
+ __name: "FullCalendarDemo",
87
+ props: {
88
+ isOccupied: { type: Boolean, default: false },
89
+ allowOverlap: { type: Boolean, default: true },
90
+ userid: { default: "" },
91
+ deviceId: { default: "" },
92
+ deviceInfo: { default: null },
93
+ taskid: { default: "" },
94
+ quickAddTimeRange: { default: null },
95
+ quickAddTaskName: { default: "" },
96
+ holidays: { default: null },
97
+ events: { default: () => [] }
98
+ },
99
+ emits: ["quickAddSaved", "eventAdded", "eventUpdated", "eventDeleted", "eventsChange"],
100
+ setup(__props, { emit: __emit }) {
101
+ const emit = __emit;
102
+ const props = __props;
103
+ const dialogVisible = ref(false);
104
+ const editingEvent = ref(null);
105
+ const deviceDialogVisible = ref(false);
106
+ const deviceInfo = computed(() => {
107
+ if (props.deviceInfo) {
108
+ return props.deviceInfo;
109
+ }
110
+ if (props.deviceId) {
111
+ return {
112
+ id: props.deviceId,
113
+ name: props.deviceId
114
+ };
115
+ }
116
+ return null;
117
+ });
118
+ const currentUser = ref("张三");
119
+ const myEventColor = { bg: "#000000", border: "#000000", name: "黑色" };
120
+ const taskColor = { bg: "#FF0000", border: "#FF0000", name: "红色" };
121
+ const eventColors = [
122
+ { bg: "#4A90E2", border: "#4A90E2", name: "蓝色" },
123
+ // 中等蓝色
124
+ { bg: "#50C878", border: "#50C878", name: "绿色" },
125
+ // 中等绿色
126
+ { bg: "#FFA500", border: "#FFA500", name: "橙色" },
127
+ // 中等橙色
128
+ { bg: "#9370DB", border: "#9370DB", name: "紫色" },
129
+ // 中等紫色
130
+ { bg: "#20B2AA", border: "#20B2AA", name: "青色" },
131
+ // 中等青色
132
+ { bg: "#FF69B4", border: "#FF69B4", name: "粉色" },
133
+ // 中等粉色
134
+ { bg: "#4169E1", border: "#4169E1", name: "靛蓝" },
135
+ // 中等靛蓝色
136
+ { bg: "#00CED1", border: "#00CED1", name: "青绿" },
137
+ // 中等青绿色
138
+ { bg: "#FF8C00", border: "#FF8C00", name: "深橙" },
139
+ // 中等深橙色
140
+ { bg: "#FFD700", border: "#FFD700", name: "金色" },
141
+ // 中等金色
142
+ { bg: "#DA70D6", border: "#DA70D6", name: "粉紫" },
143
+ // 中等粉紫色
144
+ { bg: "#8A2BE2", border: "#8A2BE2", name: "蓝紫" },
145
+ // 中等蓝紫色
146
+ { bg: "#00FA9A", border: "#00FA9A", name: "春绿" },
147
+ // 中等春绿色
148
+ { bg: "#FF7F50", border: "#FF7F50", name: "珊瑚" },
149
+ // 中等珊瑚色
150
+ { bg: "#40E0D0", border: "#40E0D0", name: "青蓝" },
151
+ // 中等青蓝色
152
+ { bg: "#FFD700", border: "#FFD700", name: "黄色" },
153
+ // 中等黄色
154
+ { bg: "#1E90FF", border: "#1E90FF", name: "天蓝" },
155
+ // 中等天蓝色
156
+ { bg: "#32CD32", border: "#32CD32", name: "草绿" },
157
+ // 中等草绿色
158
+ { bg: "#BA55D3", border: "#BA55D3", name: "中紫" },
159
+ // 中等紫色
160
+ { bg: "#00CED1", border: "#00CED1", name: "青绿2" },
161
+ // 中等青绿色
162
+ { bg: "#87CEFA", border: "#87CEFA", name: "浅蓝" },
163
+ // 中等浅蓝色
164
+ { bg: "#FFA07A", border: "#FFA07A", name: "浅橙" },
165
+ // 中等浅橙色
166
+ { bg: "#48D1CC", border: "#48D1CC", name: "中青" },
167
+ // 中等青色
168
+ { bg: "#7B68EE", border: "#7B68EE", name: "中蓝紫" },
169
+ // 中等蓝紫色
170
+ { bg: "#00CED1", border: "#00CED1", name: "青绿3" },
171
+ // 中等青绿色
172
+ { bg: "#6495ED", border: "#6495ED", name: "矢车菊" },
173
+ // 中等矢车菊蓝
174
+ { bg: "#FF6347", border: "#FF6347", name: "番茄" },
175
+ // 中等番茄色(避开红色)
176
+ { bg: "#4682B4", border: "#4682B4", name: "钢蓝" },
177
+ // 中等钢蓝色
178
+ { bg: "#9ACD32", border: "#9ACD32", name: "黄绿" },
179
+ // 中等黄绿色
180
+ { bg: "#5F9EA0", border: "#5F9EA0", name: "青灰" }
181
+ // 中等青灰色
182
+ ];
183
+ function isEventEditable(event) {
184
+ var _a;
185
+ if (!props.userid) {
186
+ return true;
187
+ }
188
+ const eventOrganizer = ((_a = event.extendedProps) == null ? void 0 : _a.organizer) || "";
189
+ return eventOrganizer === props.userid;
190
+ }
191
+ const events = ref([...props.events]);
192
+ watch(
193
+ () => props.events,
194
+ (newEvents) => {
195
+ events.value = [...newEvents || []];
196
+ },
197
+ { deep: true, immediate: true }
198
+ );
199
+ const editableEvents = computed(() => {
200
+ return events.value.map((event) => {
201
+ var _a;
202
+ const eventOrganizer = ((_a = event.extendedProps) == null ? void 0 : _a.organizer) || "";
203
+ let editable = !props.userid || eventOrganizer === props.userid;
204
+ let eventStart = "";
205
+ if (typeof event.start === "string") {
206
+ eventStart = event.start;
207
+ } else if (event.start instanceof Date) {
208
+ eventStart = event.start.toISOString();
209
+ } else if (event.start) {
210
+ eventStart = new Date(event.start).toISOString();
211
+ }
212
+ if (eventStart && isPastDateTime(eventStart)) {
213
+ editable = false;
214
+ }
215
+ let backgroundColor = event.backgroundColor;
216
+ let borderColor = event.borderColor;
217
+ if (!props.taskid) {
218
+ backgroundColor = myEventColor.bg;
219
+ borderColor = myEventColor.border;
220
+ } else if (event.id === props.taskid) {
221
+ backgroundColor = taskColor.bg;
222
+ borderColor = taskColor.border;
223
+ } else {
224
+ const colorIndex = getTaskColorIndex(event.id);
225
+ backgroundColor = eventColors[colorIndex].bg;
226
+ borderColor = eventColors[colorIndex].border;
227
+ }
228
+ return {
229
+ ...event,
230
+ editable,
231
+ backgroundColor,
232
+ borderColor
233
+ };
234
+ });
235
+ });
236
+ const formData = reactive({
237
+ title: "",
238
+ start: "",
239
+ end: "",
240
+ allDay: false,
241
+ description: "",
242
+ organizer: "",
243
+ taskid: "",
244
+ deviceId: "",
245
+ userid: ""
246
+ });
247
+ function isPastDateTime(dateStr) {
248
+ if (!dateStr) return false;
249
+ const selectedDate = new Date(dateStr);
250
+ const today = /* @__PURE__ */ new Date();
251
+ today.setHours(0, 0, 0, 0);
252
+ const selectedDateStart = new Date(selectedDate);
253
+ selectedDateStart.setHours(0, 0, 0, 0);
254
+ return selectedDateStart < today;
255
+ }
256
+ function disabledPastDate(time) {
257
+ const today = /* @__PURE__ */ new Date();
258
+ today.setHours(0, 0, 0, 0);
259
+ return time.getTime() < today.getTime();
260
+ }
261
+ function handleDateSelect(selectInfo) {
262
+ if (props.isOccupied) {
263
+ ElMessage.warning("当前处于占用状态,无法新增事件");
264
+ return;
265
+ }
266
+ if (isPastDateTime(selectInfo.startStr)) {
267
+ ElMessage.warning("不能选择今天之前的时间进行预约");
268
+ return;
269
+ }
270
+ let startStr = selectInfo.startStr;
271
+ let endStr = selectInfo.endStr;
272
+ if (!startStr && selectInfo.start) {
273
+ startStr = selectInfo.start.toISOString();
274
+ }
275
+ if (!endStr && selectInfo.end) {
276
+ endStr = selectInfo.end.toISOString();
277
+ }
278
+ let formattedStart = "";
279
+ let formattedEnd = "";
280
+ if (selectInfo.start) {
281
+ const startDate = selectInfo.start instanceof Date ? selectInfo.start : new Date(selectInfo.start);
282
+ if (!isNaN(startDate.getTime())) {
283
+ const isoStr = startDate.toISOString();
284
+ formattedStart = formatFromISO(isoStr, true);
285
+ const dateMatch = formattedStart.match(/^(\d{4}-\d{2}-\d{2})/);
286
+ if (dateMatch) {
287
+ formattedStart = dateMatch[1] + " 00:00:00";
288
+ }
289
+ } else if (startStr) {
290
+ formattedStart = formatFromISO(startStr, true);
291
+ if (formattedStart && formattedStart.match(/^\d{4}-\d{2}-\d{2}$/)) {
292
+ formattedStart = formattedStart + " 00:00:00";
293
+ } else if (formattedStart && formattedStart.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/)) {
294
+ formattedStart = formattedStart + ":00";
295
+ } else if (formattedStart && !formattedStart.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/)) {
296
+ const dateMatch = formattedStart.match(/^(\d{4}-\d{2}-\d{2})/);
297
+ if (dateMatch) {
298
+ formattedStart = dateMatch[1] + " 00:00:00";
299
+ }
300
+ }
301
+ }
302
+ } else if (startStr) {
303
+ formattedStart = formatFromISO(startStr, true);
304
+ if (formattedStart && formattedStart.match(/^\d{4}-\d{2}-\d{2}$/)) {
305
+ formattedStart = formattedStart + " 00:00:00";
306
+ } else if (formattedStart && formattedStart.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/)) {
307
+ formattedStart = formattedStart + ":00";
308
+ } else if (formattedStart && !formattedStart.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/)) {
309
+ const dateMatch = formattedStart.match(/^(\d{4}-\d{2}-\d{2})/);
310
+ if (dateMatch) {
311
+ formattedStart = dateMatch[1] + " 00:00:00";
312
+ }
313
+ }
314
+ }
315
+ if (selectInfo.end) {
316
+ const endDate = selectInfo.end instanceof Date ? selectInfo.end : new Date(selectInfo.end);
317
+ if (!isNaN(endDate.getTime())) {
318
+ const isoStr = endDate.toISOString();
319
+ formattedEnd = formatFromISO(isoStr, true);
320
+ const dateMatch = formattedEnd.match(/^(\d{4}-\d{2}-\d{2})/);
321
+ if (dateMatch) {
322
+ formattedEnd = dateMatch[1] + " 00:00:00";
323
+ }
324
+ } else if (endStr) {
325
+ formattedEnd = formatFromISO(endStr, true);
326
+ if (formattedEnd && formattedEnd.match(/^\d{4}-\d{2}-\d{2}$/)) {
327
+ formattedEnd = formattedEnd + " 00:00:00";
328
+ } else if (formattedEnd && formattedEnd.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/)) {
329
+ formattedEnd = formattedEnd + ":00";
330
+ } else if (formattedEnd && !formattedEnd.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/)) {
331
+ const dateMatch = formattedEnd.match(/^(\d{4}-\d{2}-\d{2})/);
332
+ if (dateMatch) {
333
+ formattedEnd = dateMatch[1] + " 00:00:00";
334
+ }
335
+ }
336
+ }
337
+ } else if (endStr) {
338
+ formattedEnd = formatFromISO(endStr, true);
339
+ if (formattedEnd && formattedEnd.match(/^\d{4}-\d{2}-\d{2}$/)) {
340
+ formattedEnd = formattedEnd + " 00:00:00";
341
+ } else if (formattedEnd && formattedEnd.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/)) {
342
+ formattedEnd = formattedEnd + ":00";
343
+ } else if (formattedEnd && !formattedEnd.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/)) {
344
+ const dateMatch = formattedEnd.match(/^(\d{4}-\d{2}-\d{2})/);
345
+ if (dateMatch) {
346
+ formattedEnd = dateMatch[1] + " 00:00:00";
347
+ }
348
+ }
349
+ }
350
+ formData.title = "";
351
+ formData.description = "";
352
+ formData.allDay = false;
353
+ formData.organizer = currentUser.value;
354
+ if (props.taskid) {
355
+ formData.taskid = props.taskid;
356
+ } else {
357
+ formData.taskid = "";
358
+ }
359
+ formData.deviceId = props.deviceId || "";
360
+ formData.userid = props.userid || "";
361
+ formData.start = formattedStart;
362
+ formData.end = formattedEnd;
363
+ editingEvent.value = null;
364
+ dialogVisible.value = true;
365
+ nextTick(() => {
366
+ if (formattedStart && !formData.start) {
367
+ formData.start = formattedStart;
368
+ }
369
+ if (formattedEnd && !formData.end) {
370
+ formData.end = formattedEnd;
371
+ }
372
+ });
373
+ }
374
+ function handleEventClick(clickInfo) {
375
+ var _a, _b;
376
+ if (!isEventEditable(clickInfo.event)) {
377
+ ElMessage.warning("您没有权限编辑此事件");
378
+ return;
379
+ }
380
+ const eventStartStr = ((_a = clickInfo.event.start) == null ? void 0 : _a.toISOString()) || "";
381
+ if (isPastDateTime(eventStartStr)) {
382
+ ElMessage.warning("今天之前的事件不能修改");
383
+ return;
384
+ }
385
+ editingEvent.value = clickInfo.event;
386
+ const eventProps = clickInfo.event.extendedProps;
387
+ formData.title = clickInfo.event.title;
388
+ formData.start = formatFromISO(eventStartStr);
389
+ formData.end = formatFromISO(((_b = clickInfo.event.end) == null ? void 0 : _b.toISOString()) || "");
390
+ formData.allDay = false;
391
+ formData.description = eventProps.description || "";
392
+ formData.organizer = eventProps.organizer || currentUser.value;
393
+ if (props.taskid) {
394
+ formData.taskid = props.taskid;
395
+ } else {
396
+ formData.taskid = "";
397
+ }
398
+ formData.deviceId = props.deviceId || "";
399
+ formData.userid = props.userid || "";
400
+ dialogVisible.value = true;
401
+ }
402
+ function handleEventDrop(dropInfo) {
403
+ if (!isEventEditable(dropInfo.event)) {
404
+ dropInfo.revert();
405
+ ElMessage.warning("您没有权限修改此事件");
406
+ return;
407
+ }
408
+ const newStart = dropInfo.event.start ? dropInfo.event.start instanceof Date ? dropInfo.event.start.toISOString() : typeof dropInfo.event.start === "string" ? dropInfo.event.start : "" : "";
409
+ const newEnd = dropInfo.event.end ? dropInfo.event.end instanceof Date ? dropInfo.event.end.toISOString() : typeof dropInfo.event.end === "string" ? dropInfo.event.end : void 0 : void 0;
410
+ const eventId = dropInfo.event.id;
411
+ if (isPastDateTime(newStart)) {
412
+ dropInfo.revert();
413
+ ElMessage.warning("不能将事件移动到今天之前的时间");
414
+ return;
415
+ }
416
+ if (!props.allowOverlap) {
417
+ if (checkTimeConflict(newStart, newEnd, eventId)) {
418
+ dropInfo.revert();
419
+ ElMessage.error("该时间段与其他事件冲突,无法移动到此位置");
420
+ return;
421
+ }
422
+ }
423
+ editingEvent.value = dropInfo.event;
424
+ const eventProps = dropInfo.event.extendedProps;
425
+ formData.title = dropInfo.event.title || "";
426
+ if (newStart) {
427
+ formData.start = formatFromISO(newStart);
428
+ } else {
429
+ formData.start = "";
430
+ }
431
+ if (newEnd) {
432
+ formData.end = formatFromISO(newEnd);
433
+ } else {
434
+ formData.end = "";
435
+ }
436
+ formData.allDay = false;
437
+ formData.description = (eventProps == null ? void 0 : eventProps.description) || "";
438
+ formData.organizer = (eventProps == null ? void 0 : eventProps.organizer) || currentUser.value;
439
+ if (props.taskid) {
440
+ formData.taskid = props.taskid;
441
+ } else {
442
+ formData.taskid = "";
443
+ }
444
+ formData.deviceId = props.deviceId || "";
445
+ formData.userid = props.userid || "";
446
+ dialogVisible.value = true;
447
+ }
448
+ function truncateTitle(title, maxLength) {
449
+ if (!title) return "";
450
+ if (title.length <= maxLength) return title;
451
+ return title.substring(0, maxLength) + "...";
452
+ }
453
+ function isMyEvent(event) {
454
+ var _a;
455
+ const eventOrganizer = ((_a = event.extendedProps) == null ? void 0 : _a.organizer) || "";
456
+ return !props.userid || eventOrganizer === props.userid;
457
+ }
458
+ function getTaskColorIndex(taskId) {
459
+ if (!taskId) return 0;
460
+ let hash = 0;
461
+ for (let i = 0; i < taskId.length; i++) {
462
+ hash = (hash << 5) - hash + taskId.charCodeAt(i);
463
+ hash = hash & hash;
464
+ }
465
+ return Math.abs(hash) % eventColors.length;
466
+ }
467
+ function formatEventDateTime(date) {
468
+ if (!date) return "-";
469
+ let dateObj;
470
+ if (date instanceof Date) {
471
+ dateObj = date;
472
+ } else if (typeof date === "string") {
473
+ dateObj = new Date(date);
474
+ } else {
475
+ dateObj = new Date(date);
476
+ }
477
+ const year = dateObj.getFullYear();
478
+ const month = String(dateObj.getMonth() + 1).padStart(2, "0");
479
+ const day = String(dateObj.getDate()).padStart(2, "0");
480
+ const hours = String(dateObj.getHours()).padStart(2, "0");
481
+ const minutes = String(dateObj.getMinutes()).padStart(2, "0");
482
+ const seconds = String(dateObj.getSeconds()).padStart(2, "0");
483
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
484
+ }
485
+ function handleEventResize(resizeInfo) {
486
+ var _a, _b, _c, _d, _e;
487
+ if (!isEventEditable(resizeInfo.event)) {
488
+ resizeInfo.revert();
489
+ ElMessage.warning("您没有权限修改此事件");
490
+ return;
491
+ }
492
+ const newStart = ((_a = resizeInfo.event.start) == null ? void 0 : _a.toISOString()) || "";
493
+ const newEnd = ((_b = resizeInfo.event.end) == null ? void 0 : _b.toISOString()) || void 0;
494
+ const eventId = resizeInfo.event.id;
495
+ if (isPastDateTime(newStart)) {
496
+ resizeInfo.revert();
497
+ ElMessage.warning("不能将事件调整到今天之前的时间");
498
+ return;
499
+ }
500
+ if (!props.allowOverlap) {
501
+ if (checkTimeConflict(newStart, newEnd, eventId)) {
502
+ resizeInfo.revert();
503
+ ElMessage.error("该时间段与其他事件冲突,无法调整到此时间");
504
+ return;
505
+ }
506
+ }
507
+ const eventOrganizer = ((_c = resizeInfo.event.extendedProps) == null ? void 0 : _c.organizer) || currentUser.value;
508
+ const eventTaskId = (_d = resizeInfo.event.extendedProps) == null ? void 0 : _d.taskid;
509
+ const colorInfo = assignEventColor(
510
+ formatFromISO(newStart),
511
+ newEnd ? formatFromISO(newEnd) : void 0,
512
+ eventId,
513
+ eventOrganizer,
514
+ eventId,
515
+ // 传入事件ID用于颜色分配
516
+ eventTaskId
517
+ // 传入 taskid 用于颜色分配
518
+ );
519
+ const eventIndex = events.value.findIndex((e) => e.id === eventId);
520
+ if (eventIndex !== -1) {
521
+ const originalEvent = events.value[eventIndex];
522
+ const eventOrganizer2 = ((_e = originalEvent.extendedProps) == null ? void 0 : _e.organizer) || "";
523
+ const editable = !props.userid || eventOrganizer2 === props.userid;
524
+ const updatedEvent = {
525
+ ...originalEvent,
526
+ start: newStart,
527
+ end: newEnd,
528
+ backgroundColor: colorInfo.bg,
529
+ borderColor: colorInfo.border,
530
+ editable,
531
+ // 根据 userid 设置可编辑性
532
+ extendedProps: {
533
+ ...originalEvent.extendedProps,
534
+ colorIndex: colorInfo.index,
535
+ deviceId: props.deviceId || ""
536
+ // 保存设备ID
537
+ }
538
+ };
539
+ emit("eventUpdated", updatedEvent);
540
+ const updatedEventsList = events.value.map(
541
+ (e) => e.id === eventId ? updatedEvent : e
542
+ );
543
+ emit("eventsChange", updatedEventsList);
544
+ }
545
+ }
546
+ function formatDate2(date) {
547
+ const year = date.getFullYear();
548
+ const month = String(date.getMonth() + 1).padStart(2, "0");
549
+ const day = String(date.getDate()).padStart(2, "0");
550
+ return `${year}-${month}-${day}`;
551
+ }
552
+ function getHolidayName(date) {
553
+ if (!props.holidays) {
554
+ return null;
555
+ }
556
+ const dateStr = formatDate2(date);
557
+ return props.holidays[dateStr] || null;
558
+ }
559
+ function dayCellContent(arg) {
560
+ const holidayName = getHolidayName(arg.date);
561
+ if (holidayName) {
562
+ const wrapper = document.createElement("div");
563
+ wrapper.style.cssText = "display: flex; flex-direction: column; align-items: flex-start; width: 100%; padding: 2px;";
564
+ const dateEl = document.createElement("div");
565
+ dateEl.textContent = String(arg.dayNumberText);
566
+ dateEl.style.cssText = "font-weight: 600; color: #f56c6c;";
567
+ wrapper.appendChild(dateEl);
568
+ const holidayEl = document.createElement("div");
569
+ holidayEl.textContent = holidayName;
570
+ holidayEl.className = "holiday-label";
571
+ holidayEl.style.cssText = "font-size: 9px; color: #f56c6c; font-weight: 500; margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; line-height: 1.2;";
572
+ wrapper.appendChild(holidayEl);
573
+ return { domNodes: [wrapper] };
574
+ }
575
+ return { html: arg.dayNumberText };
576
+ }
577
+ function dayCellDidMount(arg) {
578
+ const holidayName = getHolidayName(arg.date);
579
+ if (holidayName) {
580
+ arg.el.classList.add("holiday-day");
581
+ }
582
+ }
583
+ function getEventBackgroundColor(eventId) {
584
+ var _a;
585
+ if (!eventId) {
586
+ return props.taskid ? "#409eff" : myEventColor.bg;
587
+ }
588
+ if (props.taskid && eventId === props.taskid) {
589
+ return taskColor.bg;
590
+ }
591
+ if (!props.taskid) {
592
+ return myEventColor.bg;
593
+ }
594
+ if (props.taskid && eventId) {
595
+ const existingEvent = events.value.find((e) => {
596
+ var _a2;
597
+ const eventTaskId = (_a2 = e.extendedProps) == null ? void 0 : _a2.taskid;
598
+ return eventTaskId === props.taskid;
599
+ });
600
+ if (existingEvent) {
601
+ return taskColor.bg;
602
+ }
603
+ }
604
+ const originalEvent = events.value.find((e) => e.id === eventId);
605
+ if (originalEvent) {
606
+ const eventTaskId = (_a = originalEvent.extendedProps) == null ? void 0 : _a.taskid;
607
+ if (eventTaskId === props.taskid) {
608
+ return taskColor.bg;
609
+ }
610
+ const colorIndex2 = getTaskColorIndex(eventId);
611
+ return eventColors[colorIndex2].bg;
612
+ }
613
+ const editableEvent = editableEvents.value.find((e) => e.id === eventId);
614
+ if (editableEvent && editableEvent.backgroundColor) {
615
+ return editableEvent.backgroundColor;
616
+ }
617
+ const colorIndex = getTaskColorIndex(eventId);
618
+ return eventColors[colorIndex].bg;
619
+ }
620
+ function eventDidMount(arg) {
621
+ var _a;
622
+ if (arg.view.type === "dayGridMonth") {
623
+ const eventEl = arg.el;
624
+ const event = arg.event;
625
+ const eventId = event.id;
626
+ const backgroundColor = getEventBackgroundColor(eventId);
627
+ const borderColor = backgroundColor;
628
+ if (event.setProp) {
629
+ event.setProp("backgroundColor", backgroundColor);
630
+ event.setProp("borderColor", borderColor);
631
+ }
632
+ eventEl.style.backgroundColor = backgroundColor;
633
+ eventEl.style.borderColor = borderColor;
634
+ eventEl.style.height = "10px";
635
+ eventEl.style.minHeight = "10px";
636
+ eventEl.style.maxHeight = "10px";
637
+ eventEl.style.display = "block";
638
+ eventEl.style.visibility = "visible";
639
+ eventEl.style.opacity = "1";
640
+ eventEl.style.width = "100%";
641
+ eventEl.style.border = "none";
642
+ eventEl.style.borderRadius = "0";
643
+ eventEl.style.padding = "0";
644
+ eventEl.style.margin = "1px 0";
645
+ eventEl.style.cursor = "pointer";
646
+ const eventFrame = eventEl.querySelector(".fc-daygrid-event-frame");
647
+ if (eventFrame) {
648
+ eventFrame.style.backgroundColor = backgroundColor;
649
+ eventFrame.style.height = "10px";
650
+ eventFrame.style.minHeight = "10px";
651
+ eventFrame.style.maxHeight = "10px";
652
+ eventFrame.style.cursor = "pointer";
653
+ }
654
+ const tooltipTrigger = eventEl.querySelector(".el-tooltip__trigger") || eventEl;
655
+ if (tooltipTrigger) {
656
+ tooltipTrigger.style.display = "block";
657
+ tooltipTrigger.style.width = "100%";
658
+ tooltipTrigger.style.height = "100%";
659
+ tooltipTrigger.style.cursor = "pointer";
660
+ tooltipTrigger.style.position = "relative";
661
+ }
662
+ if (eventFrame) {
663
+ eventFrame.style.cursor = "pointer";
664
+ }
665
+ const allChildren = eventEl.querySelectorAll("*");
666
+ allChildren.forEach((child) => {
667
+ const childEl = child;
668
+ if (childEl.classList.contains("el-tooltip__trigger") || childEl.closest(".el-tooltip")) {
669
+ childEl.style.cursor = "pointer";
670
+ childEl.style.pointerEvents = "auto";
671
+ childEl.style.width = "100%";
672
+ childEl.style.height = "100%";
673
+ childEl.style.position = "absolute";
674
+ childEl.style.top = "0";
675
+ childEl.style.left = "0";
676
+ childEl.style.zIndex = "10";
677
+ } else {
678
+ childEl.style.backgroundColor = backgroundColor;
679
+ }
680
+ });
681
+ eventEl.style.position = "relative";
682
+ eventEl.style.pointerEvents = "auto";
683
+ const eventOrganizer = ((_a = event.extendedProps) == null ? void 0 : _a.organizer) || "";
684
+ const isMyEvent2 = !props.userid || eventOrganizer === props.userid;
685
+ if (isMyEvent2) {
686
+ const existingIcon = eventEl.querySelector(
687
+ ".my-event-icon"
688
+ );
689
+ if (!existingIcon) {
690
+ const iconEl = document.createElement("span");
691
+ iconEl.className = "my-event-icon";
692
+ iconEl.innerHTML = "★";
693
+ iconEl.style.cssText = `
694
+ position: absolute;
695
+ left: 2px;
696
+ top: 50%;
697
+ transform: translateY(-50%);
698
+ font-size: 8px;
699
+ color: #fff;
700
+ line-height: 1;
701
+ z-index: 20;
702
+ pointer-events: none;
703
+ font-weight: bold;
704
+ `;
705
+ eventEl.appendChild(iconEl);
706
+ }
707
+ }
708
+ }
709
+ }
710
+ const calendarOptions = computed(() => ({
711
+ plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
712
+ locale: l77,
713
+ timeZone: "Asia/Shanghai",
714
+ initialView: "dayGridMonth",
715
+ allDaySlot: false,
716
+ // 禁用全天事件槽
717
+ headerToolbar: {
718
+ left: "prev,next",
719
+ center: "title",
720
+ right: "dayGridMonth,listWeek,timeGridDay"
721
+ },
722
+ buttonText: {
723
+ today: "今天",
724
+ month: "月",
725
+ day: "日",
726
+ list: "周",
727
+ prev: "<",
728
+ next: ">"
729
+ },
730
+ height: 480,
731
+ // 限制日历高度为480px
732
+ slotDuration: "01:00:00",
733
+ // 时间轴间隔为1小时
734
+ views: {
735
+ dayGridMonth: {
736
+ height: 480,
737
+ // 限制月视图高度为480px
738
+ dayCellContent,
739
+ // 自定义日期单元格内容
740
+ dayCellDidMount,
741
+ // 日期单元格挂载后添加样式类
742
+ dayMaxEvents: false,
743
+ // 不显示"more"链接,超出的事件不显示
744
+ moreLinkClick: "popover",
745
+ // 如果显示more链接,点击时显示弹窗
746
+ eventDidMount
747
+ // 事件挂载后确保背景色显示
748
+ }
749
+ },
750
+ events: editableEvents.value,
751
+ // 使用动态计算的可编辑事件
752
+ editable: true,
753
+ selectable: !props.isOccupied,
754
+ // 占用状态时禁用日期选择
755
+ selectMirror: true,
756
+ selectAllow: (span) => {
757
+ var _a;
758
+ const startStr = ((_a = span.start) == null ? void 0 : _a.toISOString()) || "";
759
+ return !isPastDateTime(startStr);
760
+ },
761
+ dayMaxEvents: false,
762
+ // 不显示"more"链接,超出的事件不显示
763
+ weekends: true,
764
+ select: handleDateSelect,
765
+ eventClick: handleEventClick,
766
+ eventDrop: handleEventDrop,
767
+ eventResize: handleEventResize,
768
+ eventDidMount
769
+ // 事件挂载后确保背景色显示
770
+ }));
771
+ function handleDeviceClick() {
772
+ if (!props.deviceId) {
773
+ return;
774
+ }
775
+ deviceDialogVisible.value = true;
776
+ }
777
+ function handleAddEvent() {
778
+ if (props.isOccupied) {
779
+ ElMessage.warning("当前处于占用状态,无法新增事件");
780
+ return;
781
+ }
782
+ const now = /* @__PURE__ */ new Date();
783
+ const formatDateTime = (date) => {
784
+ const year = date.getFullYear();
785
+ const month = String(date.getMonth() + 1).padStart(2, "0");
786
+ const day = String(date.getDate()).padStart(2, "0");
787
+ const hours = String(date.getHours()).padStart(2, "0");
788
+ const minutes = String(date.getMinutes()).padStart(2, "0");
789
+ const seconds = String(date.getSeconds()).padStart(2, "0");
790
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
791
+ };
792
+ formData.start = formatDateTime(now);
793
+ formData.end = formatDateTime(new Date(now.getTime() + 60 * 60 * 1e3));
794
+ formData.allDay = false;
795
+ formData.title = "";
796
+ formData.description = "";
797
+ formData.organizer = currentUser.value;
798
+ editingEvent.value = null;
799
+ dialogVisible.value = true;
800
+ }
801
+ function handleQuickAdd() {
802
+ if (props.isOccupied) {
803
+ ElMessage.warning("当前处于占用状态,无法新增事件");
804
+ return;
805
+ }
806
+ if (!props.quickAddTimeRange) {
807
+ ElMessage.warning("未提供快速添加的时间段");
808
+ return;
809
+ }
810
+ if (!props.quickAddTaskName || !props.quickAddTaskName.trim()) {
811
+ ElMessage.warning("未提供快速添加的任务名称");
812
+ return;
813
+ }
814
+ const startStr = formatFromISO(props.quickAddTimeRange.start);
815
+ const endStr = formatFromISO(props.quickAddTimeRange.end);
816
+ if (isPastDateTime(startStr)) {
817
+ ElMessage.warning("不能选择今天之前的时间进行预约");
818
+ return;
819
+ }
820
+ if (checkTimeConflict(startStr, endStr)) {
821
+ ElMessage.error("该时间段与其他事件冲突,请选择其他时间");
822
+ return;
823
+ }
824
+ const eventId = `event-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
825
+ const taskId = formData.taskid || props.taskid || "";
826
+ const colorInfo = assignEventColor(
827
+ startStr,
828
+ endStr,
829
+ void 0,
830
+ currentUser.value,
831
+ eventId,
832
+ // 传入事件ID用于颜色分配
833
+ taskId
834
+ // 传入 taskid 用于颜色分配
835
+ );
836
+ const startISO = formatToISO(startStr);
837
+ const endISO = endStr ? formatToISO(endStr) : void 0;
838
+ const eventOrganizer = currentUser.value;
839
+ const editable = !props.userid || eventOrganizer === props.userid;
840
+ const newEvent = {
841
+ id: eventId,
842
+ title: props.quickAddTaskName,
843
+ start: startISO,
844
+ end: endISO,
845
+ allDay: false,
846
+ backgroundColor: colorInfo.bg,
847
+ borderColor: colorInfo.border,
848
+ editable,
849
+ extendedProps: {
850
+ description: "",
851
+ organizer: eventOrganizer,
852
+ colorIndex: colorInfo.index,
853
+ deviceId: props.deviceId || "",
854
+ taskid: props.taskid || ""
855
+ // 如果存在 taskid,保存到事件数据中
856
+ }
857
+ };
858
+ emit("quickAddSaved", newEvent);
859
+ emit("eventAdded", newEvent);
860
+ const newEventsList = [...events.value, newEvent];
861
+ emit("eventsChange", newEventsList);
862
+ }
863
+ function formatFromISO(isoStr, useBeijingTime = false) {
864
+ if (!isoStr) return "";
865
+ if (!isoStr.includes("T")) return isoStr;
866
+ try {
867
+ const date = new Date(isoStr);
868
+ if (isNaN(date.getTime())) {
869
+ let cleaned = isoStr;
870
+ if (cleaned.endsWith("Z")) {
871
+ cleaned = cleaned.slice(0, -1);
872
+ }
873
+ cleaned = cleaned.replace(/[+-]\d{2}:\d{2}$/, "");
874
+ return cleaned.replace("T", " ").split(".")[0];
875
+ }
876
+ let year, month, day, hours, minutes, seconds;
877
+ if (useBeijingTime) {
878
+ const beijingStr = date.toLocaleString("en-US", {
879
+ timeZone: "Asia/Shanghai",
880
+ year: "numeric",
881
+ month: "2-digit",
882
+ day: "2-digit",
883
+ hour: "2-digit",
884
+ minute: "2-digit",
885
+ second: "2-digit",
886
+ hour12: false
887
+ });
888
+ const parts = beijingStr.split(", ");
889
+ const datePart = parts[0].split("/");
890
+ const timePart = parts[1].split(":");
891
+ year = parseInt(datePart[2]);
892
+ month = parseInt(datePart[0]);
893
+ day = parseInt(datePart[1]);
894
+ hours = parseInt(timePart[0]);
895
+ minutes = parseInt(timePart[1]);
896
+ seconds = parseInt(timePart[2]);
897
+ } else {
898
+ year = date.getFullYear();
899
+ month = date.getMonth() + 1;
900
+ day = date.getDate();
901
+ hours = date.getHours();
902
+ minutes = date.getMinutes();
903
+ seconds = date.getSeconds();
904
+ }
905
+ return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(
906
+ 2,
907
+ "0"
908
+ )} ${String(hours).padStart(2, "0")}:${String(minutes).padStart(
909
+ 2,
910
+ "0"
911
+ )}:${String(seconds).padStart(2, "0")}`;
912
+ } catch (e) {
913
+ let cleaned = isoStr;
914
+ if (cleaned.endsWith("Z")) {
915
+ cleaned = cleaned.slice(0, -1);
916
+ }
917
+ cleaned = cleaned.replace(/[+-]\d{2}:\d{2}$/, "");
918
+ return cleaned.replace("T", " ").split(".")[0];
919
+ }
920
+ }
921
+ function formatToISO(dateStr) {
922
+ if (!dateStr) return "";
923
+ if (dateStr.includes("T")) return dateStr;
924
+ return dateStr.replace(" ", "T");
925
+ }
926
+ function hasTimeOverlap(start1, end1, start2, end2) {
927
+ const startTime1 = new Date(start1).getTime();
928
+ const endTime1 = end1 ? new Date(end1).getTime() : startTime1 + 60 * 60 * 1e3;
929
+ const startTime2 = new Date(start2).getTime();
930
+ const endTime2 = end2 ? new Date(end2).getTime() : startTime2 + 60 * 60 * 1e3;
931
+ return startTime1 < endTime2 && endTime1 > startTime2;
932
+ }
933
+ function checkTimeConflict(start, end, excludeEventId) {
934
+ if (props.allowOverlap) {
935
+ return false;
936
+ }
937
+ const startISO = formatToISO(start);
938
+ const endISO = end ? formatToISO(end) : void 0;
939
+ for (const event of events.value) {
940
+ if (excludeEventId && event.id === excludeEventId) {
941
+ continue;
942
+ }
943
+ let eventStart = "";
944
+ if (typeof event.start === "string") {
945
+ eventStart = event.start;
946
+ } else if (event.start instanceof Date) {
947
+ eventStart = event.start.toISOString();
948
+ } else if (event.start) {
949
+ eventStart = new Date(event.start).toISOString();
950
+ }
951
+ let eventEnd = void 0;
952
+ if (event.end) {
953
+ if (typeof event.end === "string") {
954
+ eventEnd = event.end;
955
+ } else if (event.end instanceof Date) {
956
+ eventEnd = event.end.toISOString();
957
+ } else {
958
+ eventEnd = new Date(event.end).toISOString();
959
+ }
960
+ }
961
+ if (!eventStart) continue;
962
+ if (hasTimeOverlap(startISO, endISO, eventStart, eventEnd)) {
963
+ return true;
964
+ }
965
+ }
966
+ return false;
967
+ }
968
+ function assignEventColor(_start, _end, _excludeEventId, _organizer, eventId, eventTaskId) {
969
+ if (!props.taskid) {
970
+ return {
971
+ ...myEventColor,
972
+ index: -1
973
+ // 使用 -1 表示黑色
974
+ };
975
+ }
976
+ if (eventId && eventId === props.taskid) {
977
+ return {
978
+ ...taskColor,
979
+ index: -2
980
+ // 使用 -2 表示红色(当前任务)
981
+ };
982
+ }
983
+ if (eventTaskId && eventTaskId === props.taskid) {
984
+ return {
985
+ ...taskColor,
986
+ index: -2
987
+ // 使用 -2 表示红色(当前任务)
988
+ };
989
+ }
990
+ if (props.taskid && eventId) {
991
+ const existingEvent = events.value.find((e) => {
992
+ var _a;
993
+ const eventTaskId2 = (_a = e.extendedProps) == null ? void 0 : _a.taskid;
994
+ return eventTaskId2 === props.taskid;
995
+ });
996
+ if (existingEvent) {
997
+ return {
998
+ ...taskColor,
999
+ index: -2
1000
+ // 使用 -2 表示红色(当前任务)
1001
+ };
1002
+ }
1003
+ }
1004
+ const taskIdForColor = eventId || `temp-${Date.now()}-${Math.random()}`;
1005
+ const colorIndex = getTaskColorIndex(taskIdForColor);
1006
+ return {
1007
+ ...eventColors[colorIndex],
1008
+ index: colorIndex
1009
+ };
1010
+ }
1011
+ function handleSave() {
1012
+ var _a, _b, _c;
1013
+ if (props.isOccupied && !editingEvent.value) {
1014
+ ElMessage.warning("当前处于占用状态,无法新增事件");
1015
+ return;
1016
+ }
1017
+ if (!formData.title.trim()) {
1018
+ ElMessage.warning("请输入事件标题");
1019
+ return;
1020
+ }
1021
+ if (!formData.start) {
1022
+ ElMessage.warning("请选择开始时间");
1023
+ return;
1024
+ }
1025
+ if (isPastDateTime(formData.start)) {
1026
+ ElMessage.warning("不能选择今天之前的时间进行预约");
1027
+ return;
1028
+ }
1029
+ const excludeEventId = (_a = editingEvent.value) == null ? void 0 : _a.id;
1030
+ if (checkTimeConflict(formData.start, formData.end, excludeEventId)) {
1031
+ ElMessage.error("该时间段与其他事件冲突,请选择其他时间");
1032
+ return;
1033
+ }
1034
+ let eventId = (_b = editingEvent.value) == null ? void 0 : _b.id;
1035
+ if (!eventId) {
1036
+ if (formData.taskid && formData.taskid.trim() && formData.taskid === props.taskid) {
1037
+ eventId = formData.taskid;
1038
+ } else {
1039
+ eventId = `event-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1040
+ }
1041
+ }
1042
+ const colorInfo = assignEventColor(
1043
+ formData.start,
1044
+ formData.end,
1045
+ (_c = editingEvent.value) == null ? void 0 : _c.id,
1046
+ formData.organizer || currentUser.value,
1047
+ eventId,
1048
+ // 传入事件ID用于颜色分配
1049
+ formData.taskid
1050
+ // 传入 taskid 用于颜色分配
1051
+ );
1052
+ if (editingEvent.value) {
1053
+ const startISO = formatToISO(formData.start);
1054
+ const endISO = formData.end ? formatToISO(formData.end) : null;
1055
+ editingEvent.value.setProp("title", formData.title);
1056
+ editingEvent.value.setStart(startISO);
1057
+ if (endISO) {
1058
+ editingEvent.value.setEnd(endISO);
1059
+ } else {
1060
+ editingEvent.value.setEnd(startISO);
1061
+ }
1062
+ editingEvent.value.setAllDay(false);
1063
+ editingEvent.value.setExtendedProp("description", formData.description);
1064
+ editingEvent.value.setExtendedProp(
1065
+ "organizer",
1066
+ formData.organizer || currentUser.value
1067
+ );
1068
+ editingEvent.value.setExtendedProp("colorIndex", colorInfo.index);
1069
+ if (props.deviceId) {
1070
+ editingEvent.value.setExtendedProp("deviceId", props.deviceId);
1071
+ }
1072
+ if (props.taskid) {
1073
+ editingEvent.value.setExtendedProp("taskid", props.taskid);
1074
+ }
1075
+ editingEvent.value.setProp("backgroundColor", colorInfo.bg);
1076
+ editingEvent.value.setProp("borderColor", colorInfo.border);
1077
+ const updatedEvent = {
1078
+ id: editingEvent.value.id,
1079
+ title: formData.title,
1080
+ start: startISO,
1081
+ end: endISO || startISO,
1082
+ allDay: false,
1083
+ backgroundColor: colorInfo.bg,
1084
+ borderColor: colorInfo.border,
1085
+ editable: !props.userid || (formData.organizer || currentUser.value) === props.userid,
1086
+ extendedProps: {
1087
+ description: formData.description,
1088
+ organizer: formData.organizer || currentUser.value,
1089
+ colorIndex: colorInfo.index,
1090
+ deviceId: props.deviceId || "",
1091
+ taskid: props.taskid || ""
1092
+ }
1093
+ };
1094
+ emit("eventUpdated", updatedEvent);
1095
+ const updatedEventsList = events.value.map(
1096
+ (e) => e.id === updatedEvent.id ? updatedEvent : e
1097
+ );
1098
+ emit("eventsChange", updatedEventsList);
1099
+ } else {
1100
+ const startISO = formatToISO(formData.start);
1101
+ const endISO = formData.end ? formatToISO(formData.end) : void 0;
1102
+ const eventOrganizer = formData.organizer || currentUser.value;
1103
+ const editable = !props.userid || eventOrganizer === props.userid;
1104
+ const newEvent = {
1105
+ id: eventId,
1106
+ // 使用之前生成的事件ID
1107
+ title: formData.title,
1108
+ start: startISO,
1109
+ end: endISO,
1110
+ allDay: false,
1111
+ backgroundColor: colorInfo.bg,
1112
+ borderColor: colorInfo.border,
1113
+ editable,
1114
+ // 根据 userid 设置可编辑性
1115
+ extendedProps: {
1116
+ description: formData.description,
1117
+ organizer: eventOrganizer,
1118
+ colorIndex: colorInfo.index,
1119
+ deviceId: props.deviceId || "",
1120
+ // 保存设备ID
1121
+ taskid: props.taskid || ""
1122
+ // 保存 taskid(如果存在)
1123
+ }
1124
+ };
1125
+ emit("eventAdded", newEvent);
1126
+ const newEventsList = [...events.value, newEvent];
1127
+ emit("eventsChange", newEventsList);
1128
+ }
1129
+ dialogVisible.value = false;
1130
+ resetForm();
1131
+ }
1132
+ function handleDelete() {
1133
+ if (editingEvent.value) {
1134
+ const eventId = editingEvent.value.id;
1135
+ editingEvent.value.remove();
1136
+ dialogVisible.value = false;
1137
+ resetForm();
1138
+ emit("eventDeleted", eventId || "");
1139
+ const newEventsList = events.value.filter((e) => e.id !== eventId);
1140
+ emit("eventsChange", newEventsList);
1141
+ }
1142
+ }
1143
+ function resetForm() {
1144
+ formData.title = "";
1145
+ formData.start = "";
1146
+ formData.end = "";
1147
+ formData.allDay = false;
1148
+ formData.description = "";
1149
+ formData.organizer = currentUser.value;
1150
+ formData.taskid = "";
1151
+ formData.deviceId = "";
1152
+ formData.userid = "";
1153
+ editingEvent.value = null;
1154
+ }
1155
+ return (_ctx, _cache) => {
1156
+ const _component_el_button = resolveComponent("el-button");
1157
+ const _component_el_icon = resolveComponent("el-icon");
1158
+ const _component_el_tooltip = resolveComponent("el-tooltip");
1159
+ const _component_el_input = resolveComponent("el-input");
1160
+ const _component_el_form_item = resolveComponent("el-form-item");
1161
+ const _component_el_date_picker = resolveComponent("el-date-picker");
1162
+ const _component_el_form = resolveComponent("el-form");
1163
+ const _component_el_dialog = resolveComponent("el-dialog");
1164
+ const _component_el_descriptions_item = resolveComponent("el-descriptions-item");
1165
+ const _component_el_tag = resolveComponent("el-tag");
1166
+ const _component_el_descriptions = resolveComponent("el-descriptions");
1167
+ const _component_el_empty = resolveComponent("el-empty");
1168
+ return openBlock(), createElementBlock("div", _hoisted_1, [
1169
+ createElementVNode("div", _hoisted_2, [
1170
+ props.deviceId ? (openBlock(), createElementBlock("div", _hoisted_3, [
1171
+ _cache[9] || (_cache[9] = createElementVNode("span", { class: "device-label" }, "设备ID:", -1)),
1172
+ createElementVNode("span", {
1173
+ class: "device-id",
1174
+ onClick: handleDeviceClick
1175
+ }, toDisplayString(props.deviceId), 1)
1176
+ ])) : createCommentVNode("", true),
1177
+ createElementVNode("div", null, [
1178
+ createVNode(_component_el_button, {
1179
+ type: "primary",
1180
+ size: "small",
1181
+ onClick: handleAddEvent,
1182
+ disabled: __props.isOccupied
1183
+ }, {
1184
+ default: withCtx(() => [..._cache[10] || (_cache[10] = [
1185
+ createTextVNode(" 添加预约 ", -1)
1186
+ ])]),
1187
+ _: 1
1188
+ }, 8, ["disabled"]),
1189
+ props.quickAddTimeRange ? (openBlock(), createBlock(_component_el_button, {
1190
+ key: 0,
1191
+ type: "success",
1192
+ size: "small",
1193
+ onClick: handleQuickAdd,
1194
+ disabled: __props.isOccupied
1195
+ }, {
1196
+ default: withCtx(() => [..._cache[11] || (_cache[11] = [
1197
+ createTextVNode(" 快速添加 ", -1)
1198
+ ])]),
1199
+ _: 1
1200
+ }, 8, ["disabled"])) : createCommentVNode("", true)
1201
+ ])
1202
+ ]),
1203
+ createVNode(unref(FullCalendar), {
1204
+ options: calendarOptions.value,
1205
+ class: "calendar"
1206
+ }, {
1207
+ eventContent: withCtx((arg) => [
1208
+ createVNode(_component_el_tooltip, {
1209
+ placement: "right",
1210
+ width: 350,
1211
+ effect: "light",
1212
+ "popper-class": "event-detail-tooltip",
1213
+ "show-after": 200
1214
+ }, {
1215
+ content: withCtx(() => {
1216
+ var _a, _b, _c;
1217
+ return [
1218
+ createElementVNode("div", _hoisted_4, [
1219
+ createElementVNode("div", _hoisted_5, [
1220
+ createElementVNode("span", _hoisted_6, toDisplayString(arg.event.title), 1)
1221
+ ]),
1222
+ createElementVNode("div", _hoisted_7, [
1223
+ createElementVNode("div", _hoisted_8, [
1224
+ createElementVNode("span", _hoisted_9, [
1225
+ createVNode(_component_el_icon, { class: "detail-icon" }, {
1226
+ default: withCtx(() => [
1227
+ createVNode(unref(Clock))
1228
+ ]),
1229
+ _: 1
1230
+ }),
1231
+ _cache[12] || (_cache[12] = createTextVNode(" 时间范围: ", -1))
1232
+ ]),
1233
+ createElementVNode("span", _hoisted_10, toDisplayString(arg.timeText), 1)
1234
+ ]),
1235
+ createElementVNode("div", _hoisted_11, [
1236
+ createElementVNode("span", _hoisted_12, [
1237
+ createVNode(_component_el_icon, { class: "detail-icon" }, {
1238
+ default: withCtx(() => [
1239
+ createVNode(unref(Calendar))
1240
+ ]),
1241
+ _: 1
1242
+ }),
1243
+ _cache[13] || (_cache[13] = createTextVNode(" 开始时间: ", -1))
1244
+ ]),
1245
+ createElementVNode("span", _hoisted_13, toDisplayString(formatEventDateTime(arg.event.start)), 1)
1246
+ ]),
1247
+ arg.event.end ? (openBlock(), createElementBlock("div", _hoisted_14, [
1248
+ createElementVNode("span", _hoisted_15, [
1249
+ createVNode(_component_el_icon, { class: "detail-icon" }, {
1250
+ default: withCtx(() => [
1251
+ createVNode(unref(Calendar))
1252
+ ]),
1253
+ _: 1
1254
+ }),
1255
+ _cache[14] || (_cache[14] = createTextVNode(" 结束时间: ", -1))
1256
+ ]),
1257
+ createElementVNode("span", _hoisted_16, toDisplayString(formatEventDateTime(arg.event.end)), 1)
1258
+ ])) : createCommentVNode("", true),
1259
+ ((_a = arg.event.extendedProps) == null ? void 0 : _a.organizer) ? (openBlock(), createElementBlock("div", _hoisted_17, [
1260
+ createElementVNode("span", _hoisted_18, [
1261
+ createVNode(_component_el_icon, { class: "detail-icon" }, {
1262
+ default: withCtx(() => [
1263
+ createVNode(unref(User))
1264
+ ]),
1265
+ _: 1
1266
+ }),
1267
+ _cache[15] || (_cache[15] = createTextVNode(" 预定人: ", -1))
1268
+ ]),
1269
+ createElementVNode("span", _hoisted_19, toDisplayString(arg.event.extendedProps.organizer), 1)
1270
+ ])) : createCommentVNode("", true),
1271
+ ((_b = arg.event.extendedProps) == null ? void 0 : _b.deviceId) ? (openBlock(), createElementBlock("div", _hoisted_20, [
1272
+ createElementVNode("span", _hoisted_21, [
1273
+ createVNode(_component_el_icon, { class: "detail-icon" }, {
1274
+ default: withCtx(() => [
1275
+ createVNode(unref(Monitor))
1276
+ ]),
1277
+ _: 1
1278
+ }),
1279
+ _cache[16] || (_cache[16] = createTextVNode(" 设备ID: ", -1))
1280
+ ]),
1281
+ createElementVNode("span", _hoisted_22, toDisplayString(arg.event.extendedProps.deviceId), 1)
1282
+ ])) : createCommentVNode("", true),
1283
+ ((_c = arg.event.extendedProps) == null ? void 0 : _c.description) ? (openBlock(), createElementBlock("div", _hoisted_23, [
1284
+ createElementVNode("span", _hoisted_24, [
1285
+ createVNode(_component_el_icon, { class: "detail-icon" }, {
1286
+ default: withCtx(() => [
1287
+ createVNode(unref(Document))
1288
+ ]),
1289
+ _: 1
1290
+ }),
1291
+ _cache[17] || (_cache[17] = createTextVNode(" 描述: ", -1))
1292
+ ]),
1293
+ createElementVNode("span", _hoisted_25, toDisplayString(arg.event.extendedProps.description), 1)
1294
+ ])) : createCommentVNode("", true)
1295
+ ])
1296
+ ])
1297
+ ];
1298
+ }),
1299
+ default: withCtx(() => [
1300
+ createElementVNode("div", _hoisted_26, [
1301
+ isMyEvent(arg.event) ? (openBlock(), createBlock(_component_el_icon, {
1302
+ key: 0,
1303
+ class: "my-event-marker"
1304
+ }, {
1305
+ default: withCtx(() => [
1306
+ createVNode(unref(User))
1307
+ ]),
1308
+ _: 1
1309
+ })) : createCommentVNode("", true),
1310
+ createElementVNode("span", _hoisted_27, toDisplayString(arg.timeText), 1),
1311
+ createElementVNode("span", _hoisted_28, toDisplayString(truncateTitle(arg.event.title, 10)), 1)
1312
+ ])
1313
+ ]),
1314
+ _: 2
1315
+ }, 1024)
1316
+ ]),
1317
+ _: 1
1318
+ }, 8, ["options"]),
1319
+ createVNode(_component_el_dialog, {
1320
+ modelValue: dialogVisible.value,
1321
+ "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => dialogVisible.value = $event),
1322
+ title: editingEvent.value ? "编辑预约" : "添加预约",
1323
+ width: "520px",
1324
+ class: "event-dialog",
1325
+ "close-on-click-modal": false
1326
+ }, {
1327
+ footer: withCtx(() => [
1328
+ createVNode(_component_el_button, {
1329
+ onClick: _cache[5] || (_cache[5] = ($event) => dialogVisible.value = false)
1330
+ }, {
1331
+ default: withCtx(() => [..._cache[18] || (_cache[18] = [
1332
+ createTextVNode("取消", -1)
1333
+ ])]),
1334
+ _: 1
1335
+ }),
1336
+ editingEvent.value ? (openBlock(), createBlock(_component_el_button, {
1337
+ key: 0,
1338
+ type: "danger",
1339
+ onClick: handleDelete
1340
+ }, {
1341
+ default: withCtx(() => [..._cache[19] || (_cache[19] = [
1342
+ createTextVNode("删除", -1)
1343
+ ])]),
1344
+ _: 1
1345
+ })) : createCommentVNode("", true),
1346
+ createVNode(_component_el_button, {
1347
+ type: "primary",
1348
+ onClick: handleSave
1349
+ }, {
1350
+ default: withCtx(() => [..._cache[20] || (_cache[20] = [
1351
+ createTextVNode("保存", -1)
1352
+ ])]),
1353
+ _: 1
1354
+ })
1355
+ ]),
1356
+ default: withCtx(() => [
1357
+ createVNode(_component_el_form, {
1358
+ model: formData,
1359
+ "label-width": "80px"
1360
+ }, {
1361
+ default: withCtx(() => [
1362
+ createVNode(_component_el_form_item, { label: "预定人" }, {
1363
+ default: withCtx(() => [
1364
+ createVNode(_component_el_input, {
1365
+ modelValue: currentUser.value,
1366
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => currentUser.value = $event),
1367
+ disabled: "",
1368
+ placeholder: "当前登录人"
1369
+ }, null, 8, ["modelValue"])
1370
+ ]),
1371
+ _: 1
1372
+ }),
1373
+ createVNode(_component_el_form_item, {
1374
+ label: "标题",
1375
+ required: ""
1376
+ }, {
1377
+ default: withCtx(() => [
1378
+ createVNode(_component_el_input, {
1379
+ modelValue: formData.title,
1380
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => formData.title = $event),
1381
+ placeholder: "请输入事件标题"
1382
+ }, null, 8, ["modelValue"])
1383
+ ]),
1384
+ _: 1
1385
+ }),
1386
+ createVNode(_component_el_form_item, { label: "开始时间" }, {
1387
+ default: withCtx(() => [
1388
+ (openBlock(), createBlock(_component_el_date_picker, {
1389
+ modelValue: formData.start,
1390
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => formData.start = $event),
1391
+ type: "datetime",
1392
+ placeholder: "选择开始时间",
1393
+ format: "YYYY-MM-DD HH:mm",
1394
+ "value-format": "YYYY-MM-DD HH:mm:ss",
1395
+ "disabled-date": disabledPastDate,
1396
+ key: `start-${formData.start || "empty"}`,
1397
+ style: { "width": "100%" }
1398
+ }, null, 8, ["modelValue"]))
1399
+ ]),
1400
+ _: 1
1401
+ }),
1402
+ createVNode(_component_el_form_item, { label: "结束时间" }, {
1403
+ default: withCtx(() => [
1404
+ (openBlock(), createBlock(_component_el_date_picker, {
1405
+ modelValue: formData.end,
1406
+ "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => formData.end = $event),
1407
+ type: "datetime",
1408
+ placeholder: "选择结束时间",
1409
+ format: "YYYY-MM-DD HH:mm",
1410
+ "value-format": "YYYY-MM-DD HH:mm:ss",
1411
+ "disabled-date": disabledPastDate,
1412
+ key: `end-${formData.end || "empty"}`,
1413
+ style: { "width": "100%" }
1414
+ }, null, 8, ["modelValue"]))
1415
+ ]),
1416
+ _: 1
1417
+ }),
1418
+ createVNode(_component_el_form_item, { label: "描述" }, {
1419
+ default: withCtx(() => [
1420
+ createVNode(_component_el_input, {
1421
+ modelValue: formData.description,
1422
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => formData.description = $event),
1423
+ type: "textarea",
1424
+ rows: 3,
1425
+ placeholder: "请输入描述(可选)"
1426
+ }, null, 8, ["modelValue"])
1427
+ ]),
1428
+ _: 1
1429
+ })
1430
+ ]),
1431
+ _: 1
1432
+ }, 8, ["model"])
1433
+ ]),
1434
+ _: 1
1435
+ }, 8, ["modelValue", "title"]),
1436
+ createVNode(_component_el_dialog, {
1437
+ modelValue: deviceDialogVisible.value,
1438
+ "onUpdate:modelValue": _cache[8] || (_cache[8] = ($event) => deviceDialogVisible.value = $event),
1439
+ title: "设备详情",
1440
+ width: "600px",
1441
+ class: "device-dialog",
1442
+ "close-on-click-modal": true
1443
+ }, {
1444
+ footer: withCtx(() => [
1445
+ createVNode(_component_el_button, {
1446
+ type: "primary",
1447
+ onClick: _cache[7] || (_cache[7] = ($event) => deviceDialogVisible.value = false)
1448
+ }, {
1449
+ default: withCtx(() => [..._cache[21] || (_cache[21] = [
1450
+ createTextVNode("关闭", -1)
1451
+ ])]),
1452
+ _: 1
1453
+ })
1454
+ ]),
1455
+ default: withCtx(() => [
1456
+ deviceInfo.value ? (openBlock(), createElementBlock("div", _hoisted_29, [
1457
+ createVNode(_component_el_descriptions, {
1458
+ column: 1,
1459
+ border: ""
1460
+ }, {
1461
+ default: withCtx(() => [
1462
+ createVNode(_component_el_descriptions_item, { label: "设备ID" }, {
1463
+ default: withCtx(() => [
1464
+ createTextVNode(toDisplayString(deviceInfo.value.id), 1)
1465
+ ]),
1466
+ _: 1
1467
+ }),
1468
+ createVNode(_component_el_descriptions_item, { label: "设备名称" }, {
1469
+ default: withCtx(() => [
1470
+ createTextVNode(toDisplayString(deviceInfo.value.name), 1)
1471
+ ]),
1472
+ _: 1
1473
+ }),
1474
+ deviceInfo.value.status ? (openBlock(), createBlock(_component_el_descriptions_item, {
1475
+ key: 0,
1476
+ label: "设备状态"
1477
+ }, {
1478
+ default: withCtx(() => [
1479
+ createVNode(_component_el_tag, {
1480
+ type: deviceInfo.value.status === "在线" ? "success" : "danger"
1481
+ }, {
1482
+ default: withCtx(() => [
1483
+ createTextVNode(toDisplayString(deviceInfo.value.status), 1)
1484
+ ]),
1485
+ _: 1
1486
+ }, 8, ["type"])
1487
+ ]),
1488
+ _: 1
1489
+ })) : createCommentVNode("", true),
1490
+ deviceInfo.value.type ? (openBlock(), createBlock(_component_el_descriptions_item, {
1491
+ key: 1,
1492
+ label: "设备类型"
1493
+ }, {
1494
+ default: withCtx(() => [
1495
+ createTextVNode(toDisplayString(deviceInfo.value.type), 1)
1496
+ ]),
1497
+ _: 1
1498
+ })) : createCommentVNode("", true),
1499
+ deviceInfo.value.location ? (openBlock(), createBlock(_component_el_descriptions_item, {
1500
+ key: 2,
1501
+ label: "设备位置"
1502
+ }, {
1503
+ default: withCtx(() => [
1504
+ createTextVNode(toDisplayString(deviceInfo.value.location), 1)
1505
+ ]),
1506
+ _: 1
1507
+ })) : createCommentVNode("", true),
1508
+ deviceInfo.value.description ? (openBlock(), createBlock(_component_el_descriptions_item, {
1509
+ key: 3,
1510
+ label: "设备描述"
1511
+ }, {
1512
+ default: withCtx(() => [
1513
+ createTextVNode(toDisplayString(deviceInfo.value.description), 1)
1514
+ ]),
1515
+ _: 1
1516
+ })) : createCommentVNode("", true)
1517
+ ]),
1518
+ _: 1
1519
+ })
1520
+ ])) : (openBlock(), createElementBlock("div", _hoisted_30, [
1521
+ createVNode(_component_el_empty, { description: "暂无设备信息" })
1522
+ ]))
1523
+ ]),
1524
+ _: 1
1525
+ }, 8, ["modelValue"])
1526
+ ]);
1527
+ };
1528
+ }
1529
+ });
1530
+ const _export_sfc = (sfc, props) => {
1531
+ const target = sfc.__vccOpts || sfc;
1532
+ for (const [key, val] of props) {
1533
+ target[key] = val;
1534
+ }
1535
+ return target;
1536
+ };
1537
+ const FullCalendarDemo = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-7684c35e"]]);
1538
+ function getHolidays(year) {
1539
+ const holidays = {};
1540
+ if (year === 2026) {
1541
+ holidays["2026-01-01"] = "元旦";
1542
+ holidays["2026-01-02"] = "元旦";
1543
+ holidays["2026-01-03"] = "元旦";
1544
+ holidays["2026-02-16"] = "春节";
1545
+ holidays["2026-02-17"] = "春节";
1546
+ holidays["2026-02-18"] = "春节";
1547
+ holidays["2026-02-19"] = "春节";
1548
+ holidays["2026-02-20"] = "春节";
1549
+ holidays["2026-02-21"] = "春节";
1550
+ holidays["2026-02-22"] = "春节";
1551
+ holidays["2026-04-04"] = "清明节";
1552
+ holidays["2026-04-05"] = "清明节";
1553
+ holidays["2026-04-06"] = "清明节";
1554
+ holidays["2026-05-01"] = "劳动节";
1555
+ holidays["2026-05-02"] = "劳动节";
1556
+ holidays["2026-05-03"] = "劳动节";
1557
+ holidays["2026-05-04"] = "劳动节";
1558
+ holidays["2026-05-05"] = "劳动节";
1559
+ holidays["2026-06-19"] = "端午节";
1560
+ holidays["2026-06-20"] = "端午节";
1561
+ holidays["2026-06-21"] = "端午节";
1562
+ holidays["2026-09-25"] = "中秋节";
1563
+ holidays["2026-09-26"] = "中秋节";
1564
+ holidays["2026-09-27"] = "中秋节";
1565
+ holidays["2026-10-01"] = "国庆节";
1566
+ holidays["2026-10-02"] = "国庆节";
1567
+ holidays["2026-10-03"] = "国庆节";
1568
+ holidays["2026-10-04"] = "国庆节";
1569
+ holidays["2026-10-05"] = "国庆节";
1570
+ holidays["2026-10-06"] = "国庆节";
1571
+ holidays["2026-10-07"] = "国庆节";
1572
+ holidays["2026-10-08"] = "国庆节";
1573
+ }
1574
+ return holidays;
1575
+ }
1576
+ function isHoliday(date) {
1577
+ const dateObj = new Date(date);
1578
+ const year = dateObj.getFullYear();
1579
+ const holidays = getHolidays(year);
1580
+ return holidays[date] || null;
1581
+ }
1582
+ function formatDate(date) {
1583
+ const year = date.getFullYear();
1584
+ const month = String(date.getMonth() + 1).padStart(2, "0");
1585
+ const day = String(date.getDate()).padStart(2, "0");
1586
+ return `${year}-${month}-${day}`;
1587
+ }
1588
+ function install(app) {
1589
+ app.component("FullCalendarDemo", FullCalendarDemo);
1590
+ }
1591
+ export {
1592
+ FullCalendarDemo,
1593
+ FullCalendarDemo as default,
1594
+ formatDate,
1595
+ getHolidays,
1596
+ install,
1597
+ isHoliday
1598
+ };
1599
+ //# sourceMappingURL=calendar.js.map