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.
package/README.md ADDED
@@ -0,0 +1,368 @@
1
+ <script setup lang="ts">
2
+ import { ref } from "vue";
3
+ import { ElMessage } from "element-plus";
4
+ import FullCalendarDemo from "./components/FullCalendarDemo.vue";
5
+ import type { EventInput } from "@fullcalendar/core";
6
+
7
+ // 控制占用状态(示例:可以从其他地方获取)
8
+ const isOccupied = ref(false);
9
+ // 控制是否允许时间冲突,false 表示不允许事件时间重合
10
+ const allowOverlap = ref(true);
11
+ // 当前用户ID(示例:可以从登录状态获取)
12
+ const userid = ref("张三"); // 只有 organizer 为 "张三" 的事件才能被修改
13
+
14
+ // 节假日数据:格式 { "YYYY-MM-DD": "节假日名称" }
15
+ interface HolidayData {
16
+ [date: string]: string;
17
+ }
18
+
19
+ // 2025年节假日数据
20
+ const holidays2025: HolidayData = {
21
+ // 元旦
22
+ "2025-01-01": "元旦",
23
+
24
+ // 春节
25
+ "2025-01-28": "春节",
26
+ "2025-01-29": "春节",
27
+ "2025-01-30": "春节",
28
+ "2025-01-31": "春节",
29
+ "2025-02-01": "春节",
30
+ "2025-02-02": "春节",
31
+ "2025-02-03": "春节",
32
+
33
+ // 清明节
34
+ "2025-04-04": "清明节",
35
+ "2025-04-05": "清明节",
36
+ "2025-04-06": "清明节",
37
+
38
+ // 劳动节
39
+ "2025-05-01": "劳动节",
40
+ "2025-05-02": "劳动节",
41
+ "2025-05-03": "劳动节",
42
+ "2025-05-04": "劳动节",
43
+ "2025-05-05": "劳动节",
44
+
45
+ // 端午节
46
+ "2025-05-31": "端午节",
47
+ "2025-06-01": "端午节",
48
+ "2025-06-02": "端午节",
49
+
50
+ // 中秋节
51
+ "2025-10-06": "中秋节",
52
+ "2025-10-07": "中秋节",
53
+ "2025-10-08": "中秋节",
54
+
55
+ // 国庆节
56
+ "2025-10-01": "国庆节",
57
+ "2025-10-02": "国庆节",
58
+ "2025-10-03": "国庆节",
59
+ "2025-10-04": "国庆节",
60
+ "2025-10-05": "国庆节",
61
+ };
62
+
63
+ // 2026年节假日数据
64
+ const holidays2026: HolidayData = {
65
+ // 元旦
66
+ "2026-01-01": "元旦",
67
+ "2026-01-02": "元旦",
68
+ "2026-01-03": "元旦",
69
+
70
+ // 春节
71
+ "2026-02-16": "春节",
72
+ "2026-02-17": "春节",
73
+ "2026-02-18": "春节",
74
+ "2026-02-19": "春节",
75
+ "2026-02-20": "春节",
76
+ "2026-02-21": "春节",
77
+ "2026-02-22": "春节",
78
+
79
+ // 清明节
80
+ "2026-04-04": "清明节",
81
+ "2026-04-05": "清明节",
82
+ "2026-04-06": "清明节",
83
+
84
+ // 劳动节
85
+ "2026-05-01": "劳动节",
86
+ "2026-05-02": "劳动节",
87
+ "2026-05-03": "劳动节",
88
+ "2026-05-04": "劳动节",
89
+ "2026-05-05": "劳动节",
90
+
91
+ // 端午节
92
+ "2026-06-19": "端午节",
93
+ "2026-06-20": "端午节",
94
+ "2026-06-21": "端午节",
95
+
96
+ // 中秋节
97
+ "2026-09-25": "中秋节",
98
+ "2026-09-26": "中秋节",
99
+ "2026-09-27": "中秋节",
100
+
101
+ // 国庆节
102
+ "2026-10-01": "国庆节",
103
+ "2026-10-02": "国庆节",
104
+ "2026-10-03": "国庆节",
105
+ "2026-10-04": "国庆节",
106
+ "2026-10-05": "国庆节",
107
+ "2026-10-06": "国庆节",
108
+ "2026-10-07": "国庆节",
109
+ "2026-10-08": "国庆节",
110
+ };
111
+
112
+ // 合并所有年份的节假日数据到一个对象中
113
+ const allHolidays: HolidayData = {
114
+ ...holidays2025,
115
+ ...holidays2026,
116
+ // 可以继续添加其他年份的数据,例如:
117
+ // ...holidays2027,
118
+ // ...holidays2028,
119
+ };
120
+
121
+ // 设备列表(示例:可以从API获取)
122
+ interface Device {
123
+ id: string;
124
+ name: string;
125
+ }
126
+
127
+ const devices = ref<Device[]>([
128
+ { id: "device-001", name: "设备1" },
129
+ { id: "device-002", name: "设备2" },
130
+ { id: "device-003", name: "设备3" },
131
+ ]);
132
+
133
+ // Mock 事件数据
134
+ const getMockEvents = (deviceId: string): EventInput[] => {
135
+ const today = new Date();
136
+ const todayStr = today.toISOString().split("T")[0];
137
+
138
+ return [
139
+ {
140
+ id: `${deviceId}-event-1`,
141
+ title: "团队会议",
142
+ start: `${todayStr}T10:00:00`,
143
+ end: `${todayStr}T11:30:00`,
144
+ backgroundColor: "#000000",
145
+ borderColor: "#000000",
146
+ editable: true,
147
+ extendedProps: {
148
+ organizer: "张三",
149
+ description: "每周团队例会",
150
+ deviceId: deviceId,
151
+ colorIndex: -1,
152
+ },
153
+ },
154
+ {
155
+ id: `${deviceId}-event-2`,
156
+ title: "项目评审",
157
+ start: `${todayStr}T14:00:00`,
158
+ end: `${todayStr}T16:00:00`,
159
+ backgroundColor: "#4A90E2",
160
+ borderColor: "#4A90E2",
161
+ editable: true,
162
+ extendedProps: {
163
+ organizer: "李四",
164
+ description: "项目进度评审会议",
165
+ deviceId: deviceId,
166
+ colorIndex: 0,
167
+ },
168
+ },
169
+ {
170
+ id: `${deviceId}-event-3`,
171
+ title: "客户沟通",
172
+ start: (() => {
173
+ const date = new Date(today);
174
+ date.setDate(date.getDate() + 1);
175
+ return date.toISOString().split("T")[0] + "T09:00:00";
176
+ })(),
177
+ end: (() => {
178
+ const date = new Date(today);
179
+ date.setDate(date.getDate() + 1);
180
+ return date.toISOString().split("T")[0] + "T10:30:00";
181
+ })(),
182
+ backgroundColor: "#000000",
183
+ borderColor: "#000000",
184
+ editable: true,
185
+ extendedProps: {
186
+ organizer: "张三",
187
+ description: "与重要客户进行项目沟通",
188
+ deviceId: deviceId,
189
+ colorIndex: -1,
190
+ },
191
+ },
192
+ {
193
+ id: `${deviceId}-event-4`,
194
+ title: "技术培训",
195
+ start: (() => {
196
+ const date = new Date(today);
197
+ date.setDate(date.getDate() + 1);
198
+ return date.toISOString().split("T")[0] + "T14:00:00";
199
+ })(),
200
+ end: (() => {
201
+ const date = new Date(today);
202
+ date.setDate(date.getDate() + 1);
203
+ return date.toISOString().split("T")[0] + "T17:00:00";
204
+ })(),
205
+ backgroundColor: "#50C878",
206
+ borderColor: "#50C878",
207
+ editable: true,
208
+ extendedProps: {
209
+ organizer: "王五",
210
+ description: "团队技术培训会议",
211
+ deviceId: deviceId,
212
+ colorIndex: 1,
213
+ },
214
+ },
215
+ {
216
+ id: `${deviceId}-event-5`,
217
+ title: "产品演示",
218
+ start: (() => {
219
+ const date = new Date(today);
220
+ date.setDate(date.getDate() + 2);
221
+ return date.toISOString().split("T")[0] + "T10:00:00";
222
+ })(),
223
+ end: (() => {
224
+ const date = new Date(today);
225
+ date.setDate(date.getDate() + 2);
226
+ return date.toISOString().split("T")[0] + "T11:30:00";
227
+ })(),
228
+ backgroundColor: "#FFA500",
229
+ borderColor: "#FFA500",
230
+ editable: true,
231
+ extendedProps: {
232
+ organizer: "赵六",
233
+ description: "新产品功能演示",
234
+ deviceId: deviceId,
235
+ colorIndex: 2,
236
+ },
237
+ },
238
+ {
239
+ id: `${deviceId}-event-6`,
240
+ title: "代码审查",
241
+ start: (() => {
242
+ const date = new Date(today);
243
+ date.setDate(date.getDate() + 2);
244
+ return date.toISOString().split("T")[0] + "T15:00:00";
245
+ })(),
246
+ end: (() => {
247
+ const date = new Date(today);
248
+ date.setDate(date.getDate() + 2);
249
+ return date.toISOString().split("T")[0] + "T16:30:00";
250
+ })(),
251
+ backgroundColor: "#000000",
252
+ borderColor: "#000000",
253
+ editable: true,
254
+ extendedProps: {
255
+ organizer: "张三",
256
+ description: "代码审查会议",
257
+ deviceId: deviceId,
258
+ colorIndex: -1,
259
+ },
260
+ },
261
+ ];
262
+ };
263
+
264
+ // 为每个设备创建事件列表
265
+ const deviceEvents = ref<Record<string, EventInput[]>>({
266
+ "device-001": getMockEvents("device-001"),
267
+ "device-002": getMockEvents("device-002"),
268
+ "device-003": getMockEvents("device-003"),
269
+ });
270
+
271
+ // 事件处理函数
272
+ function handleEventAdded(deviceId: string, event: EventInput) {
273
+ if (!deviceEvents.value[deviceId]) {
274
+ deviceEvents.value[deviceId] = [];
275
+ }
276
+ deviceEvents.value[deviceId].push(event);
277
+ ElMessage.success("事件已添加");
278
+ }
279
+
280
+ function handleEventUpdated(deviceId: string, event: EventInput) {
281
+ if (deviceEvents.value[deviceId]) {
282
+ const index = deviceEvents.value[deviceId].findIndex(
283
+ (e) => e.id === event.id
284
+ );
285
+ if (index !== -1) {
286
+ const originalEvent = deviceEvents.value[deviceId][index];
287
+ deviceEvents.value[deviceId][index] = event;
288
+ // 判断是调整大小还是更新(通过比较开始和结束时间)
289
+ const isResize =
290
+ originalEvent.start !== event.start || originalEvent.end !== event.end;
291
+ ElMessage.success(isResize ? "事件已调整" : "事件已更新");
292
+ } else {
293
+ ElMessage.success("事件已更新");
294
+ }
295
+ }
296
+ }
297
+
298
+ function handleEventDeleted(deviceId: string, eventId: string) {
299
+ if (deviceEvents.value[deviceId]) {
300
+ deviceEvents.value[deviceId] = deviceEvents.value[deviceId].filter(
301
+ (e) => e.id !== eventId
302
+ );
303
+ ElMessage.success("事件已删除");
304
+ }
305
+ }
306
+
307
+ function handleEventsChange(deviceId: string, events: EventInput[]) {
308
+ deviceEvents.value[deviceId] = events;
309
+ }
310
+ </script>
311
+
312
+ <template>
313
+ <div class="app-container">
314
+ <div class="calendars-grid">
315
+ <FullCalendarDemo
316
+ v-for="device in devices"
317
+ :key="device.id"
318
+ :is-occupied="isOccupied"
319
+ :allow-overlap="allowOverlap"
320
+ :userid="userid"
321
+ :device-id="device.id"
322
+ taskid="2"
323
+ :quickAddTimeRange="{
324
+ start: '2026-01-16T10:00:00',
325
+ end: '2026-01-16T12:00:00',
326
+ }"
327
+ quickAddTaskName="会议"
328
+ :holidays="allHolidays"
329
+ :events="deviceEvents[device.id] || []"
330
+ @event-added="(event) => handleEventAdded(device.id, event)"
331
+ @event-updated="(event) => handleEventUpdated(device.id, event)"
332
+ @event-deleted="(eventId) => handleEventDeleted(device.id, eventId)"
333
+ @events-change="(events) => handleEventsChange(device.id, events)"
334
+ />
335
+ </div>
336
+ </div>
337
+ </template>
338
+
339
+ <style scoped>
340
+ .app-container {
341
+ min-height: 100vh;
342
+ background-color: #f5f7fa;
343
+ width: 100vw;
344
+ }
345
+
346
+ .calendars-grid {
347
+ display: grid;
348
+ grid-template-columns: repeat(2, 1fr);
349
+ }
350
+
351
+ h1 {
352
+ text-align: center;
353
+ color: #409eff;
354
+ }
355
+
356
+ /* 响应式布局 */
357
+ @media (max-width: 1200px) {
358
+ .calendars-grid {
359
+ grid-template-columns: repeat(3, 1fr);
360
+ }
361
+ }
362
+
363
+ @media (max-width: 1900px) {
364
+ .calendars-grid {
365
+ grid-template-columns: repeat(4, 1fr);
366
+ }
367
+ }
368
+ </style>