econtrol-tools-calendar 1.0.12 → 1.0.16

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 CHANGED
@@ -11,7 +11,7 @@
11
11
  - 🏷️ 支持节假日显示
12
12
  - 🔒 支持权限控制(基于用户信息)
13
13
  - 👤 支持用户信息管理(用户 ID 和用户名)
14
- - ⚡ 支持快速添加事件
14
+ - ⚡ 支持快速添加事件(打开弹窗确认后保存)
15
15
  - 📦 TypeScript 支持
16
16
  - 🔄 单向数据流,所有操作通过事件通知父组件
17
17
 
@@ -173,18 +173,18 @@ app.mount("#app");
173
173
 
174
174
  ### Props
175
175
 
176
- | 属性名 | 类型 | 默认值 | 说明 |
177
- | ------------------- | ---------------------------------------- | ------- | ------------------------------------------------------ |
178
- | `isOccupied` | `boolean` | `false` | 是否处于占用状态,为 true 时不能新增事件 |
179
- | `allowOverlap` | `boolean` | `true` | 是否允许时间冲突,为 false 时不允许事件时间重合 |
180
- | `userinfo` | `UserInfo \| null` | `null` | 当前用户信息,包含 `userid` 和 `username` |
181
- | `deviceId` | `string` | `''` | 设备 ID,用于标识事件所属的设备(关键字段) |
182
- | `deviceInfo` | `DeviceInfo \| null` | `null` | 设备详细信息,用于显示设备名称 |
183
- | `taskid` | `string` | `''` | 当前任务 ID,匹配的事件显示红色 |
184
- | `quickAddTimeRange` | `{ start: string; end: string } \| null` | `null` | 快速添加的时间段(ISO 格式) |
185
- | `quickAddTaskName` | `string` | `''` | 快速添加的任务名称 |
186
- | `holidays` | `HolidayData \| null` | `null` | 节假日数据对象,格式: `{ "YYYY-MM-DD": "节假日名称" }` |
187
- | `events` | `EventInput[]` | `[]` | 事件列表,从外部传入的任务数据 |
176
+ | 属性名 | 类型 | 默认值 | 说明 |
177
+ | ------------------- | ---------------------------------------- | ------- | ----------------------------------------------------------------------- |
178
+ | `isOccupied` | `boolean` | `false` | 是否处于占用状态,为 true 时不能新增事件 |
179
+ | `allowOverlap` | `boolean` | `true` | 是否允许时间冲突,为 false 时不允许事件时间重合 |
180
+ | `userinfo` | `UserInfo \| null` | `null` | 当前用户信息,包含 `userid` 和 `username` |
181
+ | `deviceId` | `string` | `''` | 设备 ID,用于标识事件所属的设备(关键字段) |
182
+ | `deviceInfo` | `DeviceInfo \| null` | `null` | 设备详细信息,用于显示设备名称 |
183
+ | `taskid` | `string` | `''` | 当前任务 ID,当事件的 `extendedProps.taskid` 与此值匹配时,事件显示红色 |
184
+ | `quickAddTimeRange` | `{ start: string; end: string } \| null` | `null` | 快速添加的时间段(ISO 格式),设置后会显示"快速添加"按钮 |
185
+ | `quickAddTaskName` | `string` | `''` | 快速添加的任务名称,点击"快速添加"按钮后会填入表单 |
186
+ | `holidays` | `HolidayData \| null` | `null` | 节假日数据对象,格式: `{ "YYYY-MM-DD": "节假日名称" }` |
187
+ | `events` | `EventInput[]` | `[]` | 事件列表,从外部传入的任务数据 |
188
188
 
189
189
  #### UserInfo 接口
190
190
 
@@ -218,14 +218,14 @@ interface HolidayData {
218
218
 
219
219
  ### Events
220
220
 
221
- | 事件名 | 参数 | 说明 |
222
- | --------------- | ---------------------- | ------------------------------------ |
223
- | `quickAddSaved` | `event: EventInput` | 快速添加事件保存后触发 |
224
- | `eventAdded` | `event: EventInput` | 事件添加后触发 |
225
- | `eventUpdated` | `event: EventInput` | 事件更新后触发 |
226
- | `eventDeleted` | `event: EventInput` | 事件删除后触发,包含完整的事件详情 |
227
- | `eventsChange` | `events: EventInput[]` | 事件列表变化后触发,包含当前所有事件 |
228
- | `deviceClick` | `deviceId: string` | 点击设备名称时触发,传递设备 ID |
221
+ | 事件名 | 参数 | 说明 |
222
+ | --------------- | ---------------------- | -------------------------------------------------------------------- |
223
+ | `quickAddSaved` | `event: EventInput` | 快速添加事件保存后触发(已废弃,快速添加现在通过 `eventAdded` 触发) |
224
+ | `eventAdded` | `event: EventInput` | 事件添加后触发(包括快速添加确认保存后) |
225
+ | `eventUpdated` | `event: EventInput` | 事件更新后触发 |
226
+ | `eventDeleted` | `event: EventInput` | 事件删除后触发,包含完整的事件详情 |
227
+ | `eventsChange` | `events: EventInput[]` | 事件列表变化后触发,包含当前所有事件 |
228
+ | `deviceClick` | `deviceId: string` | 点击设备名称时触发,传递设备 ID |
229
229
 
230
230
  ### EventInput 类型
231
231
 
@@ -247,7 +247,7 @@ interface EventInput {
247
247
  organizerId?: string; // 用户ID(推荐使用)
248
248
  description?: string; // 描述
249
249
  deviceId?: string; // 设备ID(关键字段)
250
- taskid?: string; // 任务ID
250
+ taskid?: string; // 任务ID,用于匹配 props.taskid,匹配的事件会显示红色
251
251
  colorIndex?: number; // 颜色索引
252
252
  [key: string]: any;
253
253
  };
@@ -271,12 +271,12 @@ interface EventInput {
271
271
  :holidays="holidays"
272
272
  :events="events"
273
273
  :quick-add-time-range="quickAddTimeRange"
274
- quick-add-task-name="快速添加任务"
274
+ :quick-add-task-name="quickAddTaskName"
275
275
  @event-added="handleEventAdded"
276
276
  @event-updated="handleEventUpdated"
277
277
  @event-deleted="handleEventDeleted"
278
278
  @events-change="handleEventsChange"
279
- @quick-add-saved="handleQuickAddSaved"
279
+ @device-click="handleDeviceClick"
280
280
  />
281
281
  </div>
282
282
  </template>
@@ -300,6 +300,7 @@ const userinfo = ref({
300
300
  });
301
301
 
302
302
  const deviceId = ref("device-001");
303
+ // 当前任务ID:当事件的 extendedProps.taskid 与此值匹配时,事件会显示红色
303
304
  const currentTaskId = ref("task-001");
304
305
 
305
306
  // 设备信息
@@ -328,7 +329,7 @@ const events = ref<EventInput[]>([
328
329
  organizerId: "user123",
329
330
  description: "每周团队例会",
330
331
  deviceId: "device-001",
331
- taskid: "task-001",
332
+ taskid: "task-001", // 如果此值与 props.taskid 匹配,事件会显示红色
332
333
  },
333
334
  },
334
335
  ]);
@@ -337,10 +338,14 @@ const events = ref<EventInput[]>([
337
338
  const holidays = ref(getHolidays(2025));
338
339
 
339
340
  // 快速添加配置
341
+ // 设置 quickAddTimeRange 和 quickAddTaskName 后,组件会显示"快速添加"按钮
342
+ // 点击"快速添加"按钮后,会将时间段和任务名称填入表单并打开弹窗
343
+ // 用户可以在弹窗中确认或修改后点击保存,保存后会触发 eventAdded 事件
340
344
  const quickAddTimeRange = ref({
341
345
  start: "2025-01-16T10:00:00",
342
346
  end: "2025-01-16T12:00:00",
343
347
  });
348
+ const quickAddTaskName = ref("快速添加任务");
344
349
 
345
350
  // 事件处理函数
346
351
  function handleEventAdded(event: EventInput) {
@@ -382,11 +387,6 @@ function handleDeviceClick(deviceId: string) {
382
387
  // 可以在这里处理设备点击逻辑,比如打开设备详情页面、跳转路由等
383
388
  // router.push(`/device/${deviceId}`);
384
389
  }
385
-
386
- function handleQuickAddSaved(event: EventInput) {
387
- console.log("快速添加的事件:", event);
388
- // 处理快速添加逻辑
389
- }
390
390
  </script>
391
391
  ```
392
392
 
@@ -400,6 +400,7 @@ function handleQuickAddSaved(event: EventInput) {
400
400
  :key="device.id"
401
401
  :device-id="device.id"
402
402
  :device-info="device"
403
+ :taskid="currentTaskId"
403
404
  :events="deviceEvents[device.id] || []"
404
405
  :userinfo="userinfo"
405
406
  @event-added="(event) => handleEventAdded(device.id, event)"
@@ -413,17 +414,43 @@ function handleQuickAddSaved(event: EventInput) {
413
414
 
414
415
  <script setup lang="ts">
415
416
  import { ref } from "vue";
417
+ import { ElMessage } from "element-plus";
416
418
  import { SCalendarComponent } from "econtrol-tools-calendar";
417
419
  import type { EventInput } from "@fullcalendar/core";
418
420
 
421
+ // 当前任务ID:匹配的事件会显示红色
422
+ const currentTaskId = ref("task-001");
423
+
419
424
  const devices = ref([
420
425
  { id: "device-001", name: "设备1" },
421
426
  { id: "device-002", name: "设备2" },
422
427
  ]);
423
428
 
424
429
  const deviceEvents = ref<Record<string, EventInput[]>>({
425
- "device-001": [],
426
- "device-002": [],
430
+ "device-001": [
431
+ {
432
+ id: "event-1",
433
+ title: "会议1",
434
+ start: "2025-01-15T10:00:00",
435
+ end: "2025-01-15T11:00:00",
436
+ extendedProps: {
437
+ taskid: "task-001", // 匹配 currentTaskId,会显示红色
438
+ deviceId: "device-001",
439
+ },
440
+ },
441
+ ],
442
+ "device-002": [
443
+ {
444
+ id: "event-2",
445
+ title: "会议2",
446
+ start: "2025-01-15T14:00:00",
447
+ end: "2025-01-15T15:00:00",
448
+ extendedProps: {
449
+ taskid: "task-002", // 不匹配 currentTaskId,显示其他颜色
450
+ deviceId: "device-002",
451
+ },
452
+ },
453
+ ],
427
454
  });
428
455
 
429
456
  function handleEventAdded(deviceId: string, event: EventInput) {
@@ -454,7 +481,6 @@ function handleEventDeleted(deviceId: string, event: EventInput) {
454
481
  // await deleteEventFromAPI(event);
455
482
  }
456
483
  }
457
- }
458
484
 
459
485
  function handleEventsChange(deviceId: string, events: EventInput[]) {
460
486
  deviceEvents.value[deviceId] = events;
@@ -561,6 +587,27 @@ pnpm preview
561
587
 
562
588
  8. **节假日显示**:节假日数据格式为 `{ "YYYY-MM-DD": "节假日名称" }`,传入 `holidays` prop 后会在日历上高亮显示。
563
589
 
590
+ 9. **快速添加功能**:
591
+
592
+ - 设置 `quickAddTimeRange` 和 `quickAddTaskName` props 后,组件会显示"快速添加"按钮
593
+ - 点击"快速添加"按钮后,会将时间段和任务名称填入表单并打开弹窗
594
+ - 用户可以在弹窗中确认或修改后点击保存
595
+ - 保存后会触发 `eventAdded` 事件,而不是直接保存
596
+ - 如果快速添加的时间段不在当前视图范围内,日历会自动导航到该时间段
597
+
598
+ 10. **时间格式兼容**:组件支持多种时间格式输入:
599
+
600
+ - 如果传入的时间只包含日期(如 `"2025-01-15"`),会自动补全为 `"2025-01-15T00:00:00"`
601
+ - 支持 ISO 格式(`"2025-01-15T10:00:00"`)
602
+ - 支持 `YYYY-MM-DD HH:mm:ss` 格式(会自动转换为 ISO 格式)
603
+
604
+ 11. **任务 ID 和红色显示**:
605
+ - `taskid` prop 用于标识当前任务 ID
606
+ - 当事件的 `extendedProps.taskid` 字段与 `props.taskid` 匹配时,该事件会显示为红色(`#FF0000`)
607
+ - 判断逻辑:`event.extendedProps.taskid === props.taskid`
608
+ - 如果 `props.taskid` 为空字符串,所有事件显示为黑色
609
+ - 不匹配的事件会根据其 `id` 分配其他颜色
610
+
564
611
  ## 📄 许可证
565
612
 
566
613
  MIT
package/dist/calendar.js CHANGED
@@ -244,7 +244,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
244
244
  );
245
245
  const editableEvents = computed(() => {
246
246
  return events.value.map((event) => {
247
- var _a;
247
+ var _a, _b;
248
248
  const eventOrganizerId = ((_a = event.extendedProps) == null ? void 0 : _a.organizerId) || "";
249
249
  let editable = !currentUserId.value || eventOrganizerId === currentUserId.value;
250
250
  let eventStart = "";
@@ -263,13 +263,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
263
263
  if (!props.taskid) {
264
264
  backgroundColor = myEventColor.bg;
265
265
  borderColor = myEventColor.border;
266
- } else if (event.id === props.taskid) {
267
- backgroundColor = taskColor.bg;
268
- borderColor = taskColor.border;
269
266
  } else {
270
- const colorIndex = getTaskColorIndex(event.id);
271
- backgroundColor = eventColors[colorIndex].bg;
272
- borderColor = eventColors[colorIndex].border;
267
+ const eventTaskId = (_b = event.extendedProps) == null ? void 0 : _b.taskid;
268
+ if (eventTaskId === props.taskid) {
269
+ backgroundColor = taskColor.bg;
270
+ borderColor = taskColor.border;
271
+ } else {
272
+ const colorIndex = getTaskColorIndex(event.id);
273
+ backgroundColor = eventColors[colorIndex].bg;
274
+ borderColor = eventColors[colorIndex].border;
275
+ }
273
276
  }
274
277
  return {
275
278
  ...event,
@@ -643,22 +646,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
643
646
  if (!eventId) {
644
647
  return props.taskid ? "#409eff" : myEventColor.bg;
645
648
  }
646
- if (props.taskid && eventId === props.taskid) {
647
- return taskColor.bg;
648
- }
649
649
  if (!props.taskid) {
650
650
  return myEventColor.bg;
651
651
  }
652
- if (props.taskid && eventId) {
653
- const existingEvent = events.value.find((e) => {
654
- var _a2;
655
- const eventTaskId = (_a2 = e.extendedProps) == null ? void 0 : _a2.taskid;
656
- return eventTaskId === props.taskid;
657
- });
658
- if (existingEvent) {
659
- return taskColor.bg;
660
- }
661
- }
662
652
  const originalEvent = events.value.find((e) => e.id === eventId);
663
653
  if (originalEvent) {
664
654
  const eventTaskId = (_a = originalEvent.extendedProps) == null ? void 0 : _a.taskid;
@@ -878,13 +868,31 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
878
868
  ElMessage.warning("未提供快速添加的任务名称");
879
869
  return;
880
870
  }
881
- const startStr = formatFromISO(props.quickAddTimeRange.start);
882
- const endStr = formatFromISO(props.quickAddTimeRange.end);
871
+ const normalizeQuickAddTime = (time) => {
872
+ if (!time) return time;
873
+ if (/^\d{4}-\d{2}-\d{2}$/.test(time)) {
874
+ return time + "T00:00:00";
875
+ }
876
+ if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(time)) {
877
+ return time.replace(" ", "T");
878
+ }
879
+ if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(time)) {
880
+ return time.replace(" ", "T") + ":00";
881
+ }
882
+ if (time.includes("T")) {
883
+ return time;
884
+ }
885
+ return time;
886
+ };
887
+ const normalizedStart = normalizeQuickAddTime(props.quickAddTimeRange.start);
888
+ const normalizedEnd = normalizeQuickAddTime(props.quickAddTimeRange.end);
889
+ const startStr = formatFromISO(normalizedStart);
890
+ const endStr = formatFromISO(normalizedEnd);
883
891
  if (calendarRef.value) {
884
892
  try {
885
893
  const calendarApi = calendarRef.value.getApi();
886
894
  const currentView = calendarApi.view;
887
- const quickAddStartDate = new Date(props.quickAddTimeRange.start);
895
+ const quickAddStartDate = new Date(normalizedStart);
888
896
  const viewStart = new Date(currentView.activeStart);
889
897
  const viewEnd = new Date(currentView.activeEnd);
890
898
  const isInView = quickAddStartDate >= viewStart && quickAddStartDate < viewEnd;
@@ -1023,6 +1031,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1023
1031
  return false;
1024
1032
  }
1025
1033
  function assignEventColor(_start, _end, _excludeEventId, _organizer, eventId, eventTaskId) {
1034
+ var _a;
1026
1035
  if (!props.taskid) {
1027
1036
  return {
1028
1037
  ...myEventColor,
@@ -1030,13 +1039,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1030
1039
  // 使用 -1 表示黑色
1031
1040
  };
1032
1041
  }
1033
- if (eventId && eventId === props.taskid) {
1034
- return {
1035
- ...taskColor,
1036
- index: -2
1037
- // 使用 -2 表示红色(当前任务)
1038
- };
1039
- }
1040
1042
  if (eventTaskId && eventTaskId === props.taskid) {
1041
1043
  return {
1042
1044
  ...taskColor,
@@ -1045,17 +1047,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1045
1047
  };
1046
1048
  }
1047
1049
  if (props.taskid && eventId) {
1048
- const existingEvent = events.value.find((e) => {
1049
- var _a;
1050
- const eventTaskId2 = (_a = e.extendedProps) == null ? void 0 : _a.taskid;
1051
- return eventTaskId2 === props.taskid;
1052
- });
1050
+ const existingEvent = events.value.find((e) => e.id === eventId);
1053
1051
  if (existingEvent) {
1054
- return {
1055
- ...taskColor,
1056
- index: -2
1057
- // 使用 -2 表示红色(当前任务)
1058
- };
1052
+ const existingEventTaskId = (_a = existingEvent.extendedProps) == null ? void 0 : _a.taskid;
1053
+ if (existingEventTaskId === props.taskid) {
1054
+ return {
1055
+ ...taskColor,
1056
+ index: -2
1057
+ // 使用 -2 表示红色(当前任务)
1058
+ };
1059
+ }
1059
1060
  }
1060
1061
  }
1061
1062
  const taskIdForColor = eventId || `temp-${Date.now()}-${Math.random()}`;
@@ -1630,7 +1631,7 @@ const _export_sfc = (sfc, props) => {
1630
1631
  }
1631
1632
  return target;
1632
1633
  };
1633
- const SCalendarComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-3dbf0c7a"]]);
1634
+ const SCalendarComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-a553c156"]]);
1634
1635
  function getHolidays(year) {
1635
1636
  const holidays = {};
1636
1637
  if (year === 2026) {