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 +80 -33
- package/dist/calendar.js +42 -41
- package/dist/calendar.js.map +1 -1
- package/dist/calendar.umd.cjs +42 -41
- package/dist/calendar.umd.cjs.map +1 -1
- package/dist/style.css +140 -140
- package/package.json +1 -1
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
|
-
@
|
|
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
|
-
|
|
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
|
|
271
|
-
|
|
272
|
-
|
|
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
|
|
882
|
-
|
|
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(
|
|
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
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
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-
|
|
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) {
|