evui 3.3.36 → 3.3.39
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/LICENSE +21 -21
- package/README.md +40 -40
- package/dist/evui.common.js +1907 -1832
- package/dist/evui.common.js.map +1 -1
- package/dist/evui.umd.js +1907 -1832
- package/dist/evui.umd.js.map +1 -1
- package/dist/evui.umd.min.js +1 -1
- package/dist/evui.umd.min.js.map +1 -1
- package/dist/img/{EVUI.7f3588fb.svg → EVUI.b82ee81a.svg} +292 -292
- package/dist/img/{icon_mysql.7ea26d5d.svg → icon_mysql.1085fdc9.svg} +78 -78
- package/dist/img/{icon_oracle.9009b108.svg → icon_oracle.0572d3ee.svg} +13 -13
- package/dist/img/{icon_postgresql.f8fffba9.svg → icon_postgresql.ee12bde8.svg} +58 -58
- package/package.json +61 -61
- package/src/common/emitter.js +20 -20
- package/src/common/utils.debounce.js +223 -223
- package/src/common/utils.js +134 -134
- package/src/common/utils.table.js +78 -78
- package/src/common/utils.throttle.js +83 -83
- package/src/common/utils.tree.js +18 -18
- package/src/components/button/Button.vue +198 -198
- package/src/components/button/index.js +7 -7
- package/src/components/buttonGroup/ButtonGroup.vue +11 -11
- package/src/components/buttonGroup/index.js +7 -7
- package/src/components/calendar/Calendar.vue +661 -661
- package/src/components/calendar/index.js +7 -7
- package/src/components/calendar/uses.js +1272 -1272
- package/src/components/chart/Chart.vue +189 -192
- package/src/components/chart/chart.core.js +870 -870
- package/src/components/chart/element/element.bar.js +524 -524
- package/src/components/chart/element/element.bar.time.js +156 -156
- package/src/components/chart/element/element.heatmap.js +533 -533
- package/src/components/chart/element/element.line.js +339 -339
- package/src/components/chart/element/element.pie.js +197 -197
- package/src/components/chart/element/element.scatter.js +184 -184
- package/src/components/chart/element/element.tip.js +550 -542
- package/src/components/chart/helpers/helpers.canvas.js +265 -265
- package/src/components/chart/helpers/helpers.constant.js +206 -206
- package/src/components/chart/helpers/helpers.util.js +346 -338
- package/src/components/chart/index.js +9 -9
- package/src/components/chart/model/index.js +4 -4
- package/src/components/chart/model/model.series.js +93 -93
- package/src/components/chart/model/model.store.js +977 -967
- package/src/components/chart/plugins/plugins.interaction.js +769 -769
- package/src/components/chart/plugins/plugins.legend.gradient.js +602 -602
- package/src/components/chart/plugins/plugins.legend.js +1155 -1151
- package/src/components/chart/plugins/plugins.pie.js +254 -254
- package/src/components/chart/plugins/plugins.title.js +56 -56
- package/src/components/chart/plugins/plugins.tooltip.js +692 -692
- package/src/components/chart/scale/scale.js +848 -848
- package/src/components/chart/scale/scale.linear.js +38 -38
- package/src/components/chart/scale/scale.logarithmic.js +128 -128
- package/src/components/chart/scale/scale.step.js +336 -336
- package/src/components/chart/scale/scale.time.category.js +277 -277
- package/src/components/chart/scale/scale.time.js +48 -48
- package/src/components/chart/style/chart.scss +312 -312
- package/src/components/chart/uses.js +264 -252
- package/src/components/checkbox/Checkbox.vue +200 -200
- package/src/components/checkbox/index.js +7 -7
- package/src/components/checkboxGroup/CheckboxGroup.vue +44 -44
- package/src/components/checkboxGroup/index.js +7 -7
- package/src/components/contextMenu/ContextMenu.vue +80 -80
- package/src/components/contextMenu/MenuList.vue +149 -149
- package/src/components/contextMenu/index.js +7 -7
- package/src/components/contextMenu/uses.js +203 -203
- package/src/components/datePicker/DatePicker.vue +437 -437
- package/src/components/datePicker/index.js +7 -7
- package/src/components/datePicker/uses.js +419 -419
- package/src/components/grid/Grid.vue +827 -827
- package/src/components/grid/grid.filter.window.vue +493 -493
- package/src/components/grid/grid.pagination.vue +75 -75
- package/src/components/grid/grid.summary.vue +265 -265
- package/src/components/grid/grid.toolbar.vue +26 -26
- package/src/components/grid/index.js +11 -11
- package/src/components/grid/style/grid.scss +263 -263
- package/src/components/grid/uses.js +1002 -1007
- package/src/components/icon/Icon.vue +49 -49
- package/src/components/icon/index.js +8 -8
- package/src/components/inputNumber/InputNumber.vue +212 -212
- package/src/components/inputNumber/index.js +7 -7
- package/src/components/inputNumber/uses.js +217 -217
- package/src/components/loading/Loading.vue +125 -125
- package/src/components/loading/index.js +7 -7
- package/src/components/menu/Menu.vue +68 -68
- package/src/components/menu/MenuItem.vue +187 -187
- package/src/components/menu/index.js +7 -7
- package/src/components/message/Message.vue +223 -223
- package/src/components/message/index.js +31 -31
- package/src/components/messageBox/MessageBox.vue +358 -358
- package/src/components/messageBox/index.js +22 -22
- package/src/components/notification/Notification.vue +316 -316
- package/src/components/notification/index.js +49 -49
- package/src/components/pagination/Pagination.vue +271 -271
- package/src/components/pagination/index.js +7 -7
- package/src/components/pagination/pageButton.vue +30 -30
- package/src/components/progress/Progress.vue +139 -139
- package/src/components/progress/index.js +7 -7
- package/src/components/radio/Radio.vue +159 -159
- package/src/components/radio/index.js +7 -7
- package/src/components/radioGroup/RadioGroup.vue +41 -41
- package/src/components/radioGroup/index.js +7 -7
- package/src/components/scheduler/Scheduler.vue +149 -149
- package/src/components/scheduler/index.js +7 -7
- package/src/components/scheduler/uses.js +183 -183
- package/src/components/select/Select.vue +440 -440
- package/src/components/select/index.js +7 -7
- package/src/components/select/uses.js +270 -270
- package/src/components/slider/Slider.vue +505 -505
- package/src/components/slider/index.js +7 -7
- package/src/components/slider/uses.js +390 -390
- package/src/components/tabPanel/TabPanel.vue +74 -74
- package/src/components/tabPanel/index.js +7 -7
- package/src/components/tabs/Tabs.vue +517 -517
- package/src/components/tabs/index.js +7 -7
- package/src/components/textField/TextField.vue +375 -375
- package/src/components/textField/index.js +7 -7
- package/src/components/timePicker/TimePicker.vue +352 -352
- package/src/components/timePicker/index.js +7 -7
- package/src/components/toggle/Toggle.vue +115 -115
- package/src/components/toggle/index.js +7 -7
- package/src/components/tree/Tree.vue +313 -313
- package/src/components/tree/TreeNode.vue +293 -293
- package/src/components/tree/index.js +7 -7
- package/src/components/treeGrid/TreeGrid.vue +758 -758
- package/src/components/treeGrid/TreeGridNode.vue +275 -275
- package/src/components/treeGrid/index.js +9 -9
- package/src/components/treeGrid/style/treeGrid.scss +261 -261
- package/src/components/treeGrid/treeGrid.toolbar.vue +26 -26
- package/src/components/treeGrid/uses.js +867 -867
- package/src/components/window/Window.vue +329 -329
- package/src/components/window/index.js +7 -7
- package/src/components/window/uses.js +899 -899
- package/src/directives/clickoutside.js +90 -90
- package/src/main.js +116 -116
- package/src/style/components/input.scss +108 -108
- package/src/style/functions.scss +3 -3
- package/src/style/index.scss +6 -6
- package/src/style/lib/fonts/EVUI.svg +292 -292
- package/src/style/lib/icon.css +888 -888
- package/src/style/mixins.scss +94 -94
- package/src/style/themes.scss +67 -67
- package/src/style/variables.scss +22 -22
|
@@ -1,1272 +1,1272 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ref, reactive, computed, getCurrentInstance, unref, onBeforeMount, watch,
|
|
3
|
-
} from 'vue';
|
|
4
|
-
import { throttle } from 'lodash-es';
|
|
5
|
-
|
|
6
|
-
const CALENDAR_ROWS = 6;
|
|
7
|
-
const CALENDAR_COLS = 7;
|
|
8
|
-
const MONTH_CNT = 12;
|
|
9
|
-
const HOUR_CNT = 24;
|
|
10
|
-
const MIN_CNT = 60;
|
|
11
|
-
const SEC_CNT = 60;
|
|
12
|
-
const CELL_CNT_IN_ONE_PAGE = 12;
|
|
13
|
-
const CELL_CNT_IN_ONE_ROW = 4;
|
|
14
|
-
const MONTH_NAME_LIST = {
|
|
15
|
-
fullName: ['January', 'February', 'March', 'April', 'May', 'June',
|
|
16
|
-
'July', 'August', 'September', 'October', 'November', 'December'],
|
|
17
|
-
numberName: ['1', '2', '3', '4', '5', '6',
|
|
18
|
-
'7', '8', '9', '10', '11', '12'],
|
|
19
|
-
abbrName: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
|
20
|
-
'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'],
|
|
21
|
-
korName: ['1월', '2월', '3월', '4월', '5월', '6월',
|
|
22
|
-
'7월', '8월', '9월', '10월', '11월', '12월'],
|
|
23
|
-
};
|
|
24
|
-
const DAY_OF_THE_WEEK_NAME_LIST = {
|
|
25
|
-
abbrUpperName: ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'],
|
|
26
|
-
abbrLowerName: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
|
|
27
|
-
abbrPascalName: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
28
|
-
abbrKorName: ['일', '월', '화', '수', '목', '금', '토'],
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const ONE_DAY_MS = 86400000;
|
|
32
|
-
const dateReg = new RegExp(/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/);
|
|
33
|
-
const dateTimeReg = new RegExp(/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9]/);
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 배열 내 여러 날짜(eg. 'YYYY-MM-DD' || 'YYYY-MM-DD HH:MI:SS') 중 가장 끝의 날짜 텍스트 구하기
|
|
37
|
-
* @param arr
|
|
38
|
-
* @param sideDirection - 끝의 방향 (first: 가장 멀리 오래된 날짜, last: 가장 최근의 날짜)
|
|
39
|
-
* @returns {String} - 날짜 텍스트
|
|
40
|
-
*/
|
|
41
|
-
const getSideDateStr = (arr, sideDirection) => {
|
|
42
|
-
if (!arr.length) return '';
|
|
43
|
-
if (sideDirection === 'last') {
|
|
44
|
-
return arr
|
|
45
|
-
.reduce((prev, cur) => (new Date(prev).getTime() > new Date(cur).getTime() ? prev : cur));
|
|
46
|
-
}
|
|
47
|
-
return arr
|
|
48
|
-
.reduce((prev, cur) => (new Date(prev).getTime() < new Date(cur).getTime() ? prev : cur));
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* 월, 일을 두자리 숫자로 보정
|
|
53
|
-
* @param num
|
|
54
|
-
* @returns {string|*}
|
|
55
|
-
*/
|
|
56
|
-
export const lpadToTwoDigits = (num) => {
|
|
57
|
-
if (num === null) {
|
|
58
|
-
return '00';
|
|
59
|
-
} else if (+num < 10) {
|
|
60
|
-
return `0${num}`;
|
|
61
|
-
}
|
|
62
|
-
return num;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* 이차원 배열 만들기
|
|
67
|
-
* @param row
|
|
68
|
-
* @param col
|
|
69
|
-
* @returns {Array} - [row][col]
|
|
70
|
-
*/
|
|
71
|
-
const getMatrixArr = (row, col) => Array.from(Array(row), () => Array(col).fill(false));
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* y년 m월 1일의 요일 구하기
|
|
75
|
-
* @param y - 년
|
|
76
|
-
* @param m - 월
|
|
77
|
-
* @returns {number} - 해당 y년 m월 1일의 요일 (e.g. 0: SUN, ..., 6: SAT)
|
|
78
|
-
* - 1주차에서 일요일부터 1일까지의 공백 개수
|
|
79
|
-
*/
|
|
80
|
-
const getDayOfWeekOnThe1stOfMonth = (y, m) => new Date(`${y}-${m}-1`).getDay();
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* y년 m월 마지막 일자 구하기
|
|
84
|
-
* @param y
|
|
85
|
-
* @param m
|
|
86
|
-
* @returns {number} - 해당 년, 월의 마지막 일자
|
|
87
|
-
*/
|
|
88
|
-
const getLastDateOfMonth = (y, m) => {
|
|
89
|
-
let day;
|
|
90
|
-
switch (m) {
|
|
91
|
-
case 4:
|
|
92
|
-
case 6:
|
|
93
|
-
case 9:
|
|
94
|
-
case 11:
|
|
95
|
-
day = 30;
|
|
96
|
-
break;
|
|
97
|
-
case 2:
|
|
98
|
-
if (((y % 4 === 0) && (y % 100 !== 0)) || (y % 400 === 0)) {
|
|
99
|
-
day = 29;
|
|
100
|
-
} else {
|
|
101
|
-
day = 28;
|
|
102
|
-
}
|
|
103
|
-
break;
|
|
104
|
-
default:
|
|
105
|
-
day = 31;
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
return day;
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* date또는 time 형태로 format string으로 조합
|
|
113
|
-
* @param year
|
|
114
|
-
* @param month
|
|
115
|
-
* @param date
|
|
116
|
-
* @param hour
|
|
117
|
-
* @param min
|
|
118
|
-
* @param sec
|
|
119
|
-
* @returns {string}
|
|
120
|
-
*/
|
|
121
|
-
const formatDateTime = ({ year, month, date, hour, min, sec }) => {
|
|
122
|
-
if (hour !== undefined && min !== undefined && sec !== undefined) {
|
|
123
|
-
return `${year}-${lpadToTwoDigits(month)}-${lpadToTwoDigits(date)} ${lpadToTwoDigits(hour)}:${lpadToTwoDigits(min)}:${lpadToTwoDigits(sec)}`;
|
|
124
|
-
}
|
|
125
|
-
return `${year}-${lpadToTwoDigits(month)}-${lpadToTwoDigits(date)}`;
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* 첫번째 인자로 받은 날짜 형식 String ('YYYY-MM-DD' || 'YYYY-MM-DD HH:MI:SS')이나
|
|
130
|
-
* 해당 날짜형식이 들어있는 Array를 받아서 최신날짜의 정보를 추출하는 함수
|
|
131
|
-
* typeToImport가 존재하는 경우 해당 timeType의 값을
|
|
132
|
-
* typeToImport가 존재하지 않는 경우 최신날짜 텍스트를 timeType별로 분할한 Object를 리턴
|
|
133
|
-
* @param param {String | Array} - 변경하려는 날짜
|
|
134
|
-
* @param typeToImport
|
|
135
|
-
* @returns {object|number}
|
|
136
|
-
*/
|
|
137
|
-
const getDateTimeInfoByType = (param, typeToImport) => {
|
|
138
|
-
let str = unref(param);
|
|
139
|
-
if (Array.isArray(str)) {
|
|
140
|
-
str = getSideDateStr(param, 'last');
|
|
141
|
-
}
|
|
142
|
-
const result = {
|
|
143
|
-
year: +(str?.split(' ')[0]?.split('-')[0]) || null,
|
|
144
|
-
month: +(str?.split(' ')[0]?.split('-')[1]) || null,
|
|
145
|
-
date: +(str?.split(' ')[0]?.split('-')[2]) || null,
|
|
146
|
-
hour: +(str?.split(' ')[1]?.split(':')[0]) || 0,
|
|
147
|
-
min: +(str?.split(' ')[1]?.split(':')[1]) || 0,
|
|
148
|
-
sec: +(str?.split(' ')[1]?.split(':')[2]) || 0,
|
|
149
|
-
};
|
|
150
|
-
if (typeToImport === 'year') return result.year;
|
|
151
|
-
if (typeToImport === 'month') return result.month;
|
|
152
|
-
if (typeToImport === 'date') return result.date;
|
|
153
|
-
if (typeToImport === 'hour') return result.hour;
|
|
154
|
-
if (typeToImport === 'min') return result.min;
|
|
155
|
-
if (typeToImport === 'sec') return result.sec;
|
|
156
|
-
return result;
|
|
157
|
-
};
|
|
158
|
-
/**
|
|
159
|
-
* 이전달, 다음달의 달력 상 연도, 월 정보 구하기
|
|
160
|
-
* @param prevNext - 이전, 다음 여부 ('prev'|'next')
|
|
161
|
-
* @param year
|
|
162
|
-
* @param month
|
|
163
|
-
* @returns {{month: number, year: *}}
|
|
164
|
-
*/
|
|
165
|
-
const getSideMonthCalendarInfo = (prevNext, year, month) => {
|
|
166
|
-
if (prevNext === 'next') {
|
|
167
|
-
return {
|
|
168
|
-
year: month === 12 ? year + 1 : year,
|
|
169
|
-
month: ((month + 1) % 12) || 12,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
return {
|
|
173
|
-
year: month === 1 ? year - 1 : year,
|
|
174
|
-
month: ((month - 1) % 12) || 12,
|
|
175
|
-
};
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* timeFormat을 체크하여 timeFormat이 있으면 format에 맞는 형식으로 반환
|
|
180
|
-
* @param timeFormat -- props.option?.timeFormat
|
|
181
|
-
* @param dateTimeValue
|
|
182
|
-
* @param typeToImport
|
|
183
|
-
* @returns {Object|number}
|
|
184
|
-
*/
|
|
185
|
-
const getTimeInfoByTimeFormat = (timeFormat, dateTimeValue, typeToImport) => {
|
|
186
|
-
const value = getDateTimeInfoByType(dateTimeValue, typeToImport);
|
|
187
|
-
if (timeFormat) {
|
|
188
|
-
const hour = timeFormat?.split(':')[0];
|
|
189
|
-
const min = timeFormat?.split(':')[1];
|
|
190
|
-
const sec = timeFormat?.split(':')[2];
|
|
191
|
-
if (typeToImport === 'hour') {
|
|
192
|
-
return hour === 'HH' ? value : +hour;
|
|
193
|
-
} else if (typeToImport === 'min') {
|
|
194
|
-
return min === 'mm' ? value : +min;
|
|
195
|
-
} else if (typeToImport === 'sec') {
|
|
196
|
-
return sec === 'ss' ? value : +sec;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return value;
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* 초기 timeFormat에 따른 modelValue update 함수
|
|
204
|
-
* @param timeFormat - props.options.timeFormat
|
|
205
|
-
* @param modelValue
|
|
206
|
-
* @returns string
|
|
207
|
-
*/
|
|
208
|
-
export const getChangedValueByTimeFormat = (timeFormat, modelValue) => {
|
|
209
|
-
if (!modelValue) {
|
|
210
|
-
return '';
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const hourByTimeFormat = lpadToTwoDigits(getTimeInfoByTimeFormat(timeFormat, modelValue, 'hour'));
|
|
214
|
-
const minByTimeFormat = lpadToTwoDigits(getTimeInfoByTimeFormat(timeFormat, modelValue, 'min'));
|
|
215
|
-
const secByTimeFormat = lpadToTwoDigits(getTimeInfoByTimeFormat(timeFormat, modelValue, 'sec'));
|
|
216
|
-
|
|
217
|
-
return `${modelValue.split(' ')[0]} ${hourByTimeFormat}:${minByTimeFormat}:${secByTimeFormat}`;
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
const compareFromAndToDateTime = (mode, calendarType, targetDate, modelValue) => {
|
|
221
|
-
if (!modelValue.length) {
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
let fromDate = calendarType === 'main' ? targetDate : modelValue[0];
|
|
225
|
-
let toDate = calendarType === 'expanded' ? targetDate : modelValue[1];
|
|
226
|
-
|
|
227
|
-
let fromDateTime = fromDate;
|
|
228
|
-
let toDateTime = toDate;
|
|
229
|
-
if (!targetDate.split(' ')[1]) {
|
|
230
|
-
if (mode === 'dateTimeRange') {
|
|
231
|
-
fromDate = fromDate.split(' ')[0];
|
|
232
|
-
toDate = toDate.split(' ')[0];
|
|
233
|
-
const fromTime = modelValue[0].split(' ')[1];
|
|
234
|
-
const toTime = modelValue[1].split(' ')[1];
|
|
235
|
-
fromDateTime = `${fromDate} ${fromTime}`;
|
|
236
|
-
toDateTime = `${toDate} ${toTime}`;
|
|
237
|
-
} else {
|
|
238
|
-
fromDateTime = `${fromDate} 00:00:00`;
|
|
239
|
-
toDateTime = `${toDate} 23:59:59`;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return (fromDateTime && toDateTime)
|
|
244
|
-
&& new Date(fromDateTime).getTime() > +new Date(toDateTime).getTime();
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* date string 값의 MS 값 구하기
|
|
249
|
-
* @param dateStr
|
|
250
|
-
* @returns {number}
|
|
251
|
-
*/
|
|
252
|
-
const getDateMs = dateStr => new Date(`${dateStr}`).getTime();
|
|
253
|
-
|
|
254
|
-
export const useModel = () => {
|
|
255
|
-
const { props } = getCurrentInstance();
|
|
256
|
-
const timeFormat = props.options?.timeFormat;
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* 현재 선택된 값, 배열인 경우 반응형을 끊기위해 rest 사용
|
|
260
|
-
* selectValue ref로 변환하기 전 modelValue timeFormat에 따라 fetch
|
|
261
|
-
* 1) props.mode: 'date' or 'dateTime' > String
|
|
262
|
-
* 2) props.mode: 'dateMulti' or 'dateRange' > [...Array]
|
|
263
|
-
*/
|
|
264
|
-
let selectedValue;
|
|
265
|
-
if (props.mode !== 'dateMulti' && props.mode !== 'dateRange' && props.mode !== 'dateTimeRange') {
|
|
266
|
-
if (props.modelValue
|
|
267
|
-
&& ((props.modelValue.length === 10 && dateReg.exec(props.modelValue?.toString()))
|
|
268
|
-
|| (props.modelValue.length === 19 && dateTimeReg.exec(props.modelValue?.toString())))
|
|
269
|
-
) {
|
|
270
|
-
if (props.mode === 'dateTime' && timeFormat) {
|
|
271
|
-
const modelValue = getChangedValueByTimeFormat(timeFormat, props.modelValue);
|
|
272
|
-
selectedValue = ref(modelValue);
|
|
273
|
-
} else {
|
|
274
|
-
selectedValue = ref(props.modelValue);
|
|
275
|
-
}
|
|
276
|
-
} else {
|
|
277
|
-
selectedValue = ref('');
|
|
278
|
-
}
|
|
279
|
-
} else if (Array.isArray(props.modelValue)
|
|
280
|
-
&& props.modelValue.every(v => (
|
|
281
|
-
!v
|
|
282
|
-
|| (v.length === 10 && dateReg.exec(v))
|
|
283
|
-
|| (v.length === 19 && dateTimeReg.exec(v))
|
|
284
|
-
))
|
|
285
|
-
) {
|
|
286
|
-
if (props.mode === 'dateTimeRange' && props.modelValue.length === 2 && timeFormat) {
|
|
287
|
-
const modelValue = [];
|
|
288
|
-
modelValue.push(getChangedValueByTimeFormat(timeFormat[0], props.modelValue[0]));
|
|
289
|
-
modelValue.push(getChangedValueByTimeFormat(timeFormat[1], props.modelValue[1]));
|
|
290
|
-
selectedValue = ref([...modelValue]);
|
|
291
|
-
} else {
|
|
292
|
-
selectedValue = ref([...props.modelValue]);
|
|
293
|
-
}
|
|
294
|
-
} else {
|
|
295
|
-
selectedValue = ref([]);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* validate v-model's value
|
|
300
|
-
*/
|
|
301
|
-
const validateModelValue = () => {
|
|
302
|
-
if (props.mode === 'date' && props.modelValue && typeof props.modelValue !== 'string') {
|
|
303
|
-
console.warn('[EVUI][Calendar] When mode is \'date\', v-model must be \'String\' type.');
|
|
304
|
-
} else if (props.mode === 'dateTime' && props.modelValue && typeof props.modelValue !== 'string') {
|
|
305
|
-
console.warn('[EVUI][Calendar] When mode is \'dateTime\', v-model must be \'String\' type.');
|
|
306
|
-
} else if (props.mode === 'dateMulti' && props.modelValue && !Array.isArray(props.modelValue)) {
|
|
307
|
-
console.warn('[EVUI][Calendar] When mode is \'dateMulti\', v-model must be \'Array\' type.');
|
|
308
|
-
} else if (props.mode === 'dateRange' && props.modelValue) {
|
|
309
|
-
if (!Array.isArray(props.modelValue)) {
|
|
310
|
-
console.warn('[EVUI][Calendar] When mode is \'dateRange\', v-model must be \'Array\' type.');
|
|
311
|
-
} else if (getDateMs(`${props.modelValue[0]} 00:00:00`) > getDateMs(`${props.modelValue[1]} 00:00:00`)) {
|
|
312
|
-
console.warn('[EVUI][Calendar] When mode is \'dateRange\', fromDate must be less than toDate.');
|
|
313
|
-
}
|
|
314
|
-
} else if (props.mode === 'dateTimeRange' && props.modelValue) {
|
|
315
|
-
if (!Array.isArray(props.modelValue)) {
|
|
316
|
-
console.warn('[EVUI][Calendar] When mode is \'dateTimeRange\', v-model must be \'Array\' type.');
|
|
317
|
-
} else if (getDateMs(props.modelValue[0]) > getDateMs(props.modelValue[1])) {
|
|
318
|
-
console.warn('[EVUI][Calendar] When mode is \'dateRange\', fromDate must be less than toDate.');
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
// 메인(좌측) 달력(연, 월, 시, 분, 초) 페이징 정보
|
|
324
|
-
let mainCalendarPageInfo;
|
|
325
|
-
const mainValue = !['dateRange', 'dateTimeRange'].includes(props.mode) ? selectedValue.value : selectedValue.value[0];
|
|
326
|
-
if (mainValue?.length) {
|
|
327
|
-
mainCalendarPageInfo = reactive({
|
|
328
|
-
year: getDateTimeInfoByType(mainValue, 'year'),
|
|
329
|
-
month: getDateTimeInfoByType(mainValue, 'month'),
|
|
330
|
-
hour: Math.floor(getDateTimeInfoByType(mainValue, 'hour') / CELL_CNT_IN_ONE_PAGE) + 1 || 1,
|
|
331
|
-
min: Math.floor(getDateTimeInfoByType(mainValue, 'min') / CELL_CNT_IN_ONE_PAGE) + 1 || 1,
|
|
332
|
-
sec: Math.floor(getDateTimeInfoByType(mainValue, 'sec') / CELL_CNT_IN_ONE_PAGE) + 1 || 1,
|
|
333
|
-
});
|
|
334
|
-
} else {
|
|
335
|
-
mainCalendarPageInfo = reactive({
|
|
336
|
-
year: new Date().getFullYear(),
|
|
337
|
-
month: new Date().getMonth() + 1,
|
|
338
|
-
hour: 1,
|
|
339
|
-
min: 1,
|
|
340
|
-
sec: 1,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// 'mode: dateRange || dateTimeRange', 인 경우 확장된 달력(연, 월) 페이징 정보
|
|
345
|
-
let expandedCalendarPageInfo;
|
|
346
|
-
if ((['dateRange', 'dateTimeRange'].includes(props.mode))
|
|
347
|
-
&& Array.isArray(selectedValue.value)
|
|
348
|
-
&& selectedValue.value[1]
|
|
349
|
-
) {
|
|
350
|
-
const expandedValue = selectedValue.value[1];
|
|
351
|
-
const toDate = {
|
|
352
|
-
year: getDateTimeInfoByType(expandedValue, 'year'),
|
|
353
|
-
month: getDateTimeInfoByType(expandedValue, 'month'),
|
|
354
|
-
};
|
|
355
|
-
expandedCalendarPageInfo = reactive(toDate);
|
|
356
|
-
|
|
357
|
-
if (props.mode === 'dateTimeRange') {
|
|
358
|
-
expandedCalendarPageInfo.hour = Math.floor(getDateTimeInfoByType(expandedValue, 'hour') / CELL_CNT_IN_ONE_PAGE) + 1 || 1;
|
|
359
|
-
expandedCalendarPageInfo.min = Math.floor(getDateTimeInfoByType(expandedValue, 'min') / CELL_CNT_IN_ONE_PAGE) + 1 || 1;
|
|
360
|
-
expandedCalendarPageInfo.sec = Math.floor(getDateTimeInfoByType(expandedValue, 'sec') / CELL_CNT_IN_ONE_PAGE) + 1 || 1;
|
|
361
|
-
}
|
|
362
|
-
} else {
|
|
363
|
-
expandedCalendarPageInfo = reactive({
|
|
364
|
-
year: new Date().getFullYear(),
|
|
365
|
-
month: new Date().getMonth() + 1,
|
|
366
|
-
hour: 1,
|
|
367
|
-
min: 1,
|
|
368
|
-
sec: 1,
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// 현재 달력이 표현되는 월
|
|
373
|
-
const mainCalendarMonth = computed(() =>
|
|
374
|
-
MONTH_NAME_LIST[props.monthNotation][mainCalendarPageInfo.month - 1]);
|
|
375
|
-
// 다음페이지 달력이 표현되는 월
|
|
376
|
-
const expandedCalendarMonth = computed(() =>
|
|
377
|
-
MONTH_NAME_LIST[props.monthNotation][expandedCalendarPageInfo.month - 1]);
|
|
378
|
-
// 현재 달력에 표현되는 타입별 요일
|
|
379
|
-
const dayOfTheWeekList = computed(() =>
|
|
380
|
-
DAY_OF_THE_WEEK_NAME_LIST[props.dayOfTheWeekNotation]);
|
|
381
|
-
// mode: dateRange에 두 달력이 연속적인 경우
|
|
382
|
-
const isContinuousMonths = computed(
|
|
383
|
-
() => ['dateRange', 'dateTimeRange'].includes(props.mode)
|
|
384
|
-
&& (mainCalendarPageInfo.year === expandedCalendarPageInfo.year
|
|
385
|
-
&& mainCalendarPageInfo.month === expandedCalendarPageInfo.month),
|
|
386
|
-
);
|
|
387
|
-
|
|
388
|
-
onBeforeMount(() => {
|
|
389
|
-
validateModelValue();
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
return {
|
|
393
|
-
selectedValue,
|
|
394
|
-
mainCalendarPageInfo,
|
|
395
|
-
expandedCalendarPageInfo,
|
|
396
|
-
mainCalendarMonth,
|
|
397
|
-
expandedCalendarMonth,
|
|
398
|
-
dayOfTheWeekList,
|
|
399
|
-
isContinuousMonths,
|
|
400
|
-
};
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
export const useCalendarDate = (param) => {
|
|
404
|
-
const { props, emit } = getCurrentInstance();
|
|
405
|
-
const { selectedValue, mainCalendarPageInfo, expandedCalendarPageInfo } = param;
|
|
406
|
-
|
|
407
|
-
// 메인 달력 테이블의 날짜 정보 (6X7, 2차원배열)
|
|
408
|
-
const mainCalendarTableInfo = reactive(getMatrixArr(CALENDAR_ROWS, CALENDAR_COLS));
|
|
409
|
-
// dateRange 모드의 확장된 달력 테이블의 날짜 정보
|
|
410
|
-
const expandedCalendarTableInfo = reactive(getMatrixArr(CALENDAR_ROWS, CALENDAR_COLS));
|
|
411
|
-
// 시간박스 정보
|
|
412
|
-
const mainTimeTableInfo = reactive({
|
|
413
|
-
hour: [],
|
|
414
|
-
min: [],
|
|
415
|
-
sec: [],
|
|
416
|
-
});
|
|
417
|
-
// dateTimeRange 모드의 확장된 달력 테이블의 시간 박스 정보
|
|
418
|
-
const expandedTimeTableInfo = reactive({
|
|
419
|
-
hour: [],
|
|
420
|
-
min: [],
|
|
421
|
-
sec: [],
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* calendar setting 하기 전 선택된 날짜가 disabledDate에 포함되는지 체크
|
|
426
|
-
* @param isRangeMode
|
|
427
|
-
* @param calendarType
|
|
428
|
-
* @param disabledDate
|
|
429
|
-
*/
|
|
430
|
-
const checkDisabledDate = ({ isRangeMode, calendarType, disabledDate }) => {
|
|
431
|
-
if (isRangeMode) {
|
|
432
|
-
if (calendarType === 'main' && selectedValue.value[0]) {
|
|
433
|
-
if (disabledDate && disabledDate(new Date(selectedValue.value[0]))) {
|
|
434
|
-
selectedValue.value[0] = '';
|
|
435
|
-
emit('update:modelValue', [...selectedValue.value]);
|
|
436
|
-
}
|
|
437
|
-
} else if (calendarType === 'expanded' && selectedValue.value[1]) {
|
|
438
|
-
if (disabledDate && disabledDate(new Date(selectedValue.value[1]))) {
|
|
439
|
-
selectedValue.value[1] = '';
|
|
440
|
-
emit('update:modelValue', [...selectedValue.value]);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
} else if (props.mode === 'dateMulti') {
|
|
444
|
-
let isUpdate = false;
|
|
445
|
-
selectedValue.value.forEach((value, index) => {
|
|
446
|
-
if (disabledDate && disabledDate(new Date(value))) {
|
|
447
|
-
selectedValue.value.splice(index, 1);
|
|
448
|
-
isUpdate = true;
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
if (isUpdate) {
|
|
452
|
-
emit('update:modelValue', [...selectedValue.value]);
|
|
453
|
-
}
|
|
454
|
-
} else if (disabledDate && disabledDate(new Date(selectedValue.value))) {
|
|
455
|
-
selectedValue.value = '';
|
|
456
|
-
emit('update:modelValue', selectedValue.value);
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Dropdown Calendar 날짜 정보 세팅하기
|
|
462
|
-
* @param calendarType - 달력 종류 ('main'|'expanded')
|
|
463
|
-
*/
|
|
464
|
-
const setCalendarDate = (calendarType) => {
|
|
465
|
-
const calendarPageInfo = calendarType === 'expanded'
|
|
466
|
-
? expandedCalendarPageInfo : mainCalendarPageInfo;
|
|
467
|
-
const calendarTableInfo = calendarType === 'expanded'
|
|
468
|
-
? expandedCalendarTableInfo : mainCalendarTableInfo;
|
|
469
|
-
|
|
470
|
-
let disabledDate = props.options.disabledDate;
|
|
471
|
-
if (disabledDate && Array.isArray(disabledDate)) {
|
|
472
|
-
disabledDate = calendarType === 'main' ? disabledDate[0] : disabledDate[1];
|
|
473
|
-
}
|
|
474
|
-
const isRangeMode = ['dateRange', 'dateTimeRange'].includes(props.mode);
|
|
475
|
-
|
|
476
|
-
checkDisabledDate({
|
|
477
|
-
isRangeMode,
|
|
478
|
-
calendarType,
|
|
479
|
-
disabledDate,
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
const TODAY_YMD = formatDateTime({
|
|
483
|
-
year: new Date().getFullYear(),
|
|
484
|
-
month: new Date().getMonth() + 1,
|
|
485
|
-
date: new Date().getDate(),
|
|
486
|
-
});
|
|
487
|
-
const PREV_MONTH = computed(() =>
|
|
488
|
-
((MONTH_CNT + calendarPageInfo.month - 1) % MONTH_CNT) || MONTH_CNT);
|
|
489
|
-
const NEXT_MONTH = computed(() =>
|
|
490
|
-
((calendarPageInfo.month + 1) % MONTH_CNT) || MONTH_CNT);
|
|
491
|
-
const YEAR_OF_PREV_MONTH = computed(() => (calendarPageInfo.month === 1
|
|
492
|
-
? calendarPageInfo.year - 1 : calendarPageInfo.year));
|
|
493
|
-
const YEAR_OF_NEXT_MONTH = computed(() => (calendarPageInfo.month === 12
|
|
494
|
-
? calendarPageInfo.year + 1 : calendarPageInfo.year));
|
|
495
|
-
// 이번달 1일의 요일
|
|
496
|
-
const dayOfWeekOnThe1stOfThisMonth = computed(() => getDayOfWeekOnThe1stOfMonth(
|
|
497
|
-
calendarPageInfo.year,
|
|
498
|
-
calendarPageInfo.month,
|
|
499
|
-
));
|
|
500
|
-
// 저번달 마지막 날짜
|
|
501
|
-
const lastDateOfPrevMonth = computed(() => getLastDateOfMonth(
|
|
502
|
-
calendarPageInfo.month === 1
|
|
503
|
-
? calendarPageInfo.year - 1 : calendarPageInfo.year,
|
|
504
|
-
(MONTH_CNT + calendarPageInfo.month - 1) % MONTH_CNT || MONTH_CNT,
|
|
505
|
-
));
|
|
506
|
-
// 이번달 마지막 날짜
|
|
507
|
-
const lastDateOfThisMonth = computed(() => getLastDateOfMonth(
|
|
508
|
-
calendarPageInfo.year,
|
|
509
|
-
calendarPageInfo.month,
|
|
510
|
-
));
|
|
511
|
-
|
|
512
|
-
let modelValue = '';
|
|
513
|
-
if (props.mode.includes('Time')) {
|
|
514
|
-
if (props.mode === 'dateTime') {
|
|
515
|
-
modelValue = selectedValue.value;
|
|
516
|
-
} else {
|
|
517
|
-
modelValue = calendarType === 'main' ? selectedValue.value[0] : selectedValue.value[1];
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
let monthDate = 0;
|
|
522
|
-
let year = 0;
|
|
523
|
-
let month = 0;
|
|
524
|
-
let date = 0;
|
|
525
|
-
let currDate = '';
|
|
526
|
-
// date 숫자 및 속성 세팅
|
|
527
|
-
const setDateInfo = (monthType, i, j) => {
|
|
528
|
-
currDate = formatDateTime({ year, month, date });
|
|
529
|
-
const isInvalidDate = isRangeMode
|
|
530
|
-
&& compareFromAndToDateTime(props.mode, calendarType, currDate, selectedValue.value);
|
|
531
|
-
|
|
532
|
-
// time 모드인 경우 현재 값의 시간을 가지고 테스트
|
|
533
|
-
const timeValue = modelValue?.split(' ')[1] ?? '';
|
|
534
|
-
|
|
535
|
-
const isDisabled = disabledDate
|
|
536
|
-
? disabledDate(new Date(`${currDate} ${timeValue}`)) : isInvalidDate;
|
|
537
|
-
|
|
538
|
-
const index = +(calendarType !== 'main');
|
|
539
|
-
const isRangeSelected = isRangeMode && selectedValue.value.length > index
|
|
540
|
-
&& selectedValue.value[index].split(' ')[0].includes(currDate);
|
|
541
|
-
const isSelected = !isDisabled && (isRangeMode
|
|
542
|
-
? monthType === '' && isRangeSelected
|
|
543
|
-
: selectedValue.value?.includes(currDate));
|
|
544
|
-
|
|
545
|
-
// mode가 dateRange일 때는 이전, 다음달에 selected 를 하지 않는다.
|
|
546
|
-
calendarTableInfo[i][j] = {
|
|
547
|
-
monthType: `${monthType}${isDisabled ? ' disabled' : ''}`,
|
|
548
|
-
isToday: TODAY_YMD === currDate,
|
|
549
|
-
isSelected,
|
|
550
|
-
year,
|
|
551
|
-
month,
|
|
552
|
-
date,
|
|
553
|
-
};
|
|
554
|
-
};
|
|
555
|
-
|
|
556
|
-
for (let i = 0; i < CALENDAR_ROWS; i++) {
|
|
557
|
-
for (let j = 0; j < CALENDAR_COLS; j++) {
|
|
558
|
-
if (i === 0) {
|
|
559
|
-
// 첫번째 주
|
|
560
|
-
if (dayOfWeekOnThe1stOfThisMonth.value !== 0) {
|
|
561
|
-
if (j < dayOfWeekOnThe1stOfThisMonth.value) {
|
|
562
|
-
year = YEAR_OF_PREV_MONTH.value;
|
|
563
|
-
month = PREV_MONTH.value;
|
|
564
|
-
date = lastDateOfPrevMonth.value - dayOfWeekOnThe1stOfThisMonth.value + 1 + j;
|
|
565
|
-
setDateInfo('prev', i, j);
|
|
566
|
-
} else {
|
|
567
|
-
monthDate++;
|
|
568
|
-
year = calendarPageInfo.year;
|
|
569
|
-
month = calendarPageInfo.month;
|
|
570
|
-
date = monthDate;
|
|
571
|
-
setDateInfo('', i, j);
|
|
572
|
-
}
|
|
573
|
-
} else {
|
|
574
|
-
year = YEAR_OF_PREV_MONTH.value;
|
|
575
|
-
month = PREV_MONTH.value;
|
|
576
|
-
date = lastDateOfPrevMonth.value - 6 + j;
|
|
577
|
-
setDateInfo('prev', i, j);
|
|
578
|
-
}
|
|
579
|
-
} else if (lastDateOfThisMonth.value <= monthDate) {
|
|
580
|
-
// 마지막 -1, 마지막 주의 다음달 날짜
|
|
581
|
-
monthDate++;
|
|
582
|
-
year = YEAR_OF_NEXT_MONTH.value;
|
|
583
|
-
month = NEXT_MONTH.value;
|
|
584
|
-
date = monthDate - lastDateOfThisMonth.value;
|
|
585
|
-
setDateInfo('next', i, j);
|
|
586
|
-
} else {
|
|
587
|
-
// 첫번째 주를 제외한 이번달 날짜
|
|
588
|
-
monthDate++;
|
|
589
|
-
year = calendarPageInfo.year;
|
|
590
|
-
month = calendarPageInfo.month;
|
|
591
|
-
date = monthDate;
|
|
592
|
-
setDateInfo('', i, j);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
};
|
|
597
|
-
|
|
598
|
-
/**
|
|
599
|
-
* Calendar 시간 정보 세팅하기
|
|
600
|
-
*/
|
|
601
|
-
const setHmsTime = () => {
|
|
602
|
-
const timeFormat = props.options?.timeFormat;
|
|
603
|
-
const disabledDate = props.options?.disabledDate;
|
|
604
|
-
const mainTimeFormat = Array.isArray(timeFormat) ? timeFormat[0] : timeFormat;
|
|
605
|
-
const expandedTimeFormat = Array.isArray(timeFormat) ? timeFormat[1] : '';
|
|
606
|
-
const mainDateTimeValue = props.mode === 'dateTimeRange' ? selectedValue.value[0] : selectedValue.value;
|
|
607
|
-
const expandedDateTimeValue = props.mode === 'dateTimeRange' ? selectedValue.value[1] : '';
|
|
608
|
-
const mainDisabledDate = Array.isArray(disabledDate) ? disabledDate[0] : disabledDate;
|
|
609
|
-
const expandedDisabledDate = Array.isArray(disabledDate) ? disabledDate[1] : disabledDate;
|
|
610
|
-
|
|
611
|
-
const compareDateTimeValue = (calendarType, timeType, value) => {
|
|
612
|
-
const dateTimeValue = calendarType === 'main' ? mainDateTimeValue : expandedDateTimeValue;
|
|
613
|
-
const disabledDateFunc = calendarType === 'main' ? mainDisabledDate : expandedDisabledDate;
|
|
614
|
-
if (!dateTimeValue) {
|
|
615
|
-
return false;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
const date = dateTimeValue.split(' ')[0];
|
|
619
|
-
let hour = getDateTimeInfoByType(dateTimeValue, 'hour');
|
|
620
|
-
let min = getDateTimeInfoByType(dateTimeValue, 'min');
|
|
621
|
-
let sec = getDateTimeInfoByType(dateTimeValue, 'sec');
|
|
622
|
-
|
|
623
|
-
if (timeType === 'hour') {
|
|
624
|
-
hour = value;
|
|
625
|
-
} else if (timeType === 'min') {
|
|
626
|
-
min = value;
|
|
627
|
-
} else if (timeType === 'sec') {
|
|
628
|
-
sec = value;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
const targetDateTimeValue = `${date} ${lpadToTwoDigits(hour)}:${lpadToTwoDigits(min)}:${lpadToTwoDigits(sec)}`;
|
|
632
|
-
if (disabledDateFunc && disabledDateFunc(new Date(targetDateTimeValue))) {
|
|
633
|
-
return true;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
return !disabledDateFunc && compareFromAndToDateTime(
|
|
637
|
-
props.mode,
|
|
638
|
-
calendarType,
|
|
639
|
-
targetDateTimeValue,
|
|
640
|
-
selectedValue.value,
|
|
641
|
-
);
|
|
642
|
-
};
|
|
643
|
-
|
|
644
|
-
['hour', 'min', 'sec'].forEach((v) => {
|
|
645
|
-
let cnt = SEC_CNT;
|
|
646
|
-
if (v === 'hour') {
|
|
647
|
-
cnt = HOUR_CNT;
|
|
648
|
-
} else if (v === 'min') {
|
|
649
|
-
cnt = MIN_CNT;
|
|
650
|
-
}
|
|
651
|
-
const mainTimeValue = mainDateTimeValue && mainDateTimeValue.length > 0
|
|
652
|
-
? getTimeInfoByTimeFormat(mainTimeFormat, mainDateTimeValue, v) : -1;
|
|
653
|
-
const expandedTimeValue = expandedDateTimeValue && expandedDateTimeValue.length > 0
|
|
654
|
-
? getTimeInfoByTimeFormat(expandedTimeFormat, expandedDateTimeValue, v) : -1;
|
|
655
|
-
for (let i = 0; i < cnt; i++) {
|
|
656
|
-
let isDisabled = props.mode === 'dateTimeRange' && compareDateTimeValue('main', v, i);
|
|
657
|
-
mainTimeTableInfo[v][i] = {
|
|
658
|
-
timeType: v,
|
|
659
|
-
num: i,
|
|
660
|
-
isSelected: !isDisabled && mainTimeValue === i,
|
|
661
|
-
isDisabled,
|
|
662
|
-
};
|
|
663
|
-
if (props.mode === 'dateTimeRange') {
|
|
664
|
-
isDisabled = compareDateTimeValue('expanded', v, i);
|
|
665
|
-
expandedTimeTableInfo[v][i] = {
|
|
666
|
-
timeType: v,
|
|
667
|
-
num: i,
|
|
668
|
-
isSelected: !isDisabled && expandedTimeValue === i,
|
|
669
|
-
isDisabled,
|
|
670
|
-
};
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
});
|
|
674
|
-
};
|
|
675
|
-
|
|
676
|
-
/**
|
|
677
|
-
* HMS 영역 내 tr, td, 페이지에 맞는 시간 정보 가져오기
|
|
678
|
-
* @param timeType - {'hour'|'min'|'sec'}
|
|
679
|
-
* @param i - rows
|
|
680
|
-
* @param j - cols
|
|
681
|
-
* @param calendarType - {'main'|'expanded'}
|
|
682
|
-
* @returns {object} - cellInfo
|
|
683
|
-
*/
|
|
684
|
-
const getTimeInfo = (timeType, i, j, calendarType) => {
|
|
685
|
-
const pageInfo = calendarType === 'main' ? mainCalendarPageInfo : expandedCalendarPageInfo;
|
|
686
|
-
const timeInfo = calendarType === 'main' ? mainTimeTableInfo : expandedTimeTableInfo;
|
|
687
|
-
const currPage = pageInfo[timeType] - 1;
|
|
688
|
-
const currRowIdx = i - 1;
|
|
689
|
-
const currColIdx = j - 1;
|
|
690
|
-
const currIdx = (currPage * CELL_CNT_IN_ONE_PAGE)
|
|
691
|
-
+ (currRowIdx * CELL_CNT_IN_ONE_ROW) + currColIdx;
|
|
692
|
-
return timeInfo[timeType][currIdx];
|
|
693
|
-
};
|
|
694
|
-
|
|
695
|
-
return {
|
|
696
|
-
mainCalendarTableInfo,
|
|
697
|
-
expandedCalendarTableInfo,
|
|
698
|
-
mainTimeTableInfo,
|
|
699
|
-
expandedTimeTableInfo,
|
|
700
|
-
setCalendarDate,
|
|
701
|
-
setHmsTime,
|
|
702
|
-
getTimeInfo,
|
|
703
|
-
};
|
|
704
|
-
};
|
|
705
|
-
|
|
706
|
-
export const useEvent = (param) => {
|
|
707
|
-
const { props, emit } = getCurrentInstance();
|
|
708
|
-
const timeFormat = props.options?.timeFormat;
|
|
709
|
-
const {
|
|
710
|
-
selectedValue,
|
|
711
|
-
mainCalendarPageInfo,
|
|
712
|
-
expandedCalendarPageInfo,
|
|
713
|
-
mainTimeTableInfo,
|
|
714
|
-
expandedTimeTableInfo,
|
|
715
|
-
setCalendarDate,
|
|
716
|
-
setHmsTime,
|
|
717
|
-
} = param;
|
|
718
|
-
|
|
719
|
-
// dateRange mode에서 클릭하여 첫번째 선택된 날짜
|
|
720
|
-
const dateRangeClickedDate = ref('');
|
|
721
|
-
// dateRange mode에서 클릭한번 후 커서에 따라 날짜를 마우스오버하는 경우 dynamic argument로 이벤트명 설정
|
|
722
|
-
const calendarEventName = ref(null);
|
|
723
|
-
// dateTime 또는 dateTimeRange에서 timeFormat이 있는 경우 event 막음
|
|
724
|
-
const mainTimeFormat = Array.isArray(timeFormat) ? timeFormat[0] : timeFormat;
|
|
725
|
-
const expandedTimeFormat = Array.isArray(timeFormat) ? timeFormat[1] : '';
|
|
726
|
-
const preventTimeEventType = {
|
|
727
|
-
main: {
|
|
728
|
-
hour: mainTimeFormat && mainTimeFormat.split(':')[0] !== 'HH',
|
|
729
|
-
min: mainTimeFormat && mainTimeFormat.split(':')[1] !== 'mm',
|
|
730
|
-
sec: mainTimeFormat && mainTimeFormat.split(':')[2] !== 'ss',
|
|
731
|
-
},
|
|
732
|
-
expanded: {
|
|
733
|
-
hour: expandedTimeFormat && expandedTimeFormat.split(':')[0] !== 'HH',
|
|
734
|
-
min: expandedTimeFormat && expandedTimeFormat.split(':')[1] !== 'mm',
|
|
735
|
-
sec: expandedTimeFormat && expandedTimeFormat.split(':')[2] !== 'ss',
|
|
736
|
-
},
|
|
737
|
-
};
|
|
738
|
-
|
|
739
|
-
/**
|
|
740
|
-
* 입력받은 dateTime object에 calendar date, time 영역 페이지 세팅
|
|
741
|
-
* @param calendarType - 달력 종류 ('main'|'expanded')
|
|
742
|
-
* @param dateTime - 입력된 페이지 정보 ({ year, month, hour, min, sec })
|
|
743
|
-
*/
|
|
744
|
-
const setCalendarPageInfo = (calendarType, dateTime) => {
|
|
745
|
-
const calendarPageInfo = calendarType === 'expanded'
|
|
746
|
-
? expandedCalendarPageInfo : mainCalendarPageInfo;
|
|
747
|
-
const { year, month, hour, min, sec } = dateTime;
|
|
748
|
-
if (year) {
|
|
749
|
-
calendarPageInfo.year = year;
|
|
750
|
-
}
|
|
751
|
-
if (month) {
|
|
752
|
-
calendarPageInfo.month = month;
|
|
753
|
-
}
|
|
754
|
-
if (hour) {
|
|
755
|
-
calendarPageInfo.hour = hour;
|
|
756
|
-
}
|
|
757
|
-
if (min) {
|
|
758
|
-
calendarPageInfo.min = min;
|
|
759
|
-
}
|
|
760
|
-
if (sec) {
|
|
761
|
-
calendarPageInfo.sec = sec;
|
|
762
|
-
}
|
|
763
|
-
};
|
|
764
|
-
|
|
765
|
-
/**
|
|
766
|
-
* value를 Array로 담아 페이지 세팅
|
|
767
|
-
* @param valueList
|
|
768
|
-
*/
|
|
769
|
-
const updateCalendarPage = (valueList) => {
|
|
770
|
-
valueList?.forEach((currValue, index) => {
|
|
771
|
-
const changeCalendarType = index === 0 ? 'main' : 'expanded';
|
|
772
|
-
setCalendarPageInfo(changeCalendarType, {
|
|
773
|
-
year: getDateTimeInfoByType(currValue, 'year'),
|
|
774
|
-
month: getDateTimeInfoByType(currValue, 'month'),
|
|
775
|
-
hour: Math.floor(getDateTimeInfoByType(currValue, 'hour') / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
776
|
-
min: Math.floor(getDateTimeInfoByType(currValue, 'min') / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
777
|
-
sec: Math.floor(getDateTimeInfoByType(currValue, 'sec') / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
778
|
-
});
|
|
779
|
-
setCalendarDate(changeCalendarType);
|
|
780
|
-
});
|
|
781
|
-
};
|
|
782
|
-
|
|
783
|
-
/**
|
|
784
|
-
* Calendar 의 Month 이동시키기 (이전, 이후)
|
|
785
|
-
* expandedCalendar가 존재하는 경우(mode: timeRange)
|
|
786
|
-
* mainCalendar의 date는 expandedCalendar의 날짜를 넘길 수 없다
|
|
787
|
-
* mainCalendar year, month < expandedCalendar year, month
|
|
788
|
-
* @param calendarType - 달력 종류 ('main'|'expanded')
|
|
789
|
-
* @param type - {'prev'|'next'}
|
|
790
|
-
*/
|
|
791
|
-
const moveMonth = (calendarType, type) => {
|
|
792
|
-
const isDateRangeMode = ['dateRange', 'dateTimeRange'].includes(props.mode);
|
|
793
|
-
let calendarPageInfo = mainCalendarPageInfo;
|
|
794
|
-
if (!isDateRangeMode) {
|
|
795
|
-
if (type === 'prev') {
|
|
796
|
-
if (calendarPageInfo.month === 1) {
|
|
797
|
-
calendarPageInfo.year -= 1;
|
|
798
|
-
calendarPageInfo.month = 12;
|
|
799
|
-
} else {
|
|
800
|
-
calendarPageInfo.month -= 1;
|
|
801
|
-
}
|
|
802
|
-
} else if (calendarPageInfo.month === 12) {
|
|
803
|
-
calendarPageInfo.year += 1;
|
|
804
|
-
calendarPageInfo.month = 1;
|
|
805
|
-
} else {
|
|
806
|
-
calendarPageInfo.month += 1;
|
|
807
|
-
}
|
|
808
|
-
} else {
|
|
809
|
-
calendarPageInfo = calendarType === 'expanded'
|
|
810
|
-
? expandedCalendarPageInfo : mainCalendarPageInfo;
|
|
811
|
-
|
|
812
|
-
// 두 달력간의 연속 여부 (메인 달력 + 1Month === 확장된 달력)
|
|
813
|
-
// mainCalendar Month < expandedCalendar Month
|
|
814
|
-
const isContinuousMonths = expandedCalendarPageInfo.year === mainCalendarPageInfo.year
|
|
815
|
-
&& expandedCalendarPageInfo.month === mainCalendarPageInfo.month;
|
|
816
|
-
if (type === 'prev') {
|
|
817
|
-
if (isContinuousMonths && calendarType === 'expanded') {
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
if (calendarPageInfo.month === 1) {
|
|
821
|
-
calendarPageInfo.year -= 1;
|
|
822
|
-
calendarPageInfo.month = 12;
|
|
823
|
-
} else {
|
|
824
|
-
calendarPageInfo.month -= 1;
|
|
825
|
-
}
|
|
826
|
-
} else {
|
|
827
|
-
if (isContinuousMonths && calendarType === 'main') {
|
|
828
|
-
return;
|
|
829
|
-
}
|
|
830
|
-
if (calendarPageInfo.month === 12) {
|
|
831
|
-
calendarPageInfo.year += 1;
|
|
832
|
-
calendarPageInfo.month = 1;
|
|
833
|
-
} else {
|
|
834
|
-
calendarPageInfo.month += 1;
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
};
|
|
839
|
-
|
|
840
|
-
/**
|
|
841
|
-
* Calendar Header의 prev, next 아이콘 클릭 이벤트
|
|
842
|
-
* @param calendarType - 달력 종류 ('main'|'expanded')
|
|
843
|
-
* @param type - 이전달, 다음달 ('prev'|'next')
|
|
844
|
-
*/
|
|
845
|
-
const clickPrevNextBtn = (calendarType, type) => {
|
|
846
|
-
moveMonth(calendarType, type);
|
|
847
|
-
setCalendarDate(calendarType);
|
|
848
|
-
};
|
|
849
|
-
|
|
850
|
-
/**
|
|
851
|
-
* Calendar Date 일자 클릭 이벤트
|
|
852
|
-
* @param calendarType - {main|expanded}
|
|
853
|
-
* @param dateInfo
|
|
854
|
-
*/
|
|
855
|
-
const clickDate = (calendarType, dateInfo) => {
|
|
856
|
-
const { year, month, date, monthType } = dateInfo;
|
|
857
|
-
const CURR_DATE_STR = formatDateTime({ year, month, date });
|
|
858
|
-
const isExistCurrDate = props.modelValue ? (Array.isArray(props.modelValue)
|
|
859
|
-
? props.modelValue?.map(v => v.split(' ')[0])
|
|
860
|
-
: props.modelValue.split(' ')[0])
|
|
861
|
-
.includes(CURR_DATE_STR) : false;
|
|
862
|
-
|
|
863
|
-
let disabledDate = props.options.disabledDate;
|
|
864
|
-
if (disabledDate && Array.isArray(disabledDate)) {
|
|
865
|
-
disabledDate = calendarType === 'main' ? disabledDate[0] : disabledDate[1];
|
|
866
|
-
}
|
|
867
|
-
// 제한된 날짜는 선택할 수 없다.
|
|
868
|
-
if (disabledDate && disabledDate(new Date(CURR_DATE_STR)) && !isExistCurrDate) {
|
|
869
|
-
return;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
const calendarPageInfo = calendarType === 'main' ? mainCalendarPageInfo : expandedCalendarPageInfo;
|
|
873
|
-
const PREV_MONTH = ((MONTH_CNT + calendarPageInfo.month - 1) % MONTH_CNT) || MONTH_CNT;
|
|
874
|
-
const YEAR_OF_PREV_MONTH = calendarPageInfo.month === 1
|
|
875
|
-
? calendarPageInfo.year - 1 : calendarPageInfo.year;
|
|
876
|
-
const NEXT_MONTH = ((calendarPageInfo.month + 1) % MONTH_CNT) || MONTH_CNT;
|
|
877
|
-
const YEAR_OF_NEXT_MONTH = calendarPageInfo.month === 12
|
|
878
|
-
? calendarPageInfo.year + 1 : calendarPageInfo.year;
|
|
879
|
-
|
|
880
|
-
const moveDispCalendarMonth = () => {
|
|
881
|
-
if (monthType.includes('prev')) {
|
|
882
|
-
calendarPageInfo.year = YEAR_OF_PREV_MONTH;
|
|
883
|
-
calendarPageInfo.month = PREV_MONTH;
|
|
884
|
-
} else if (monthType.includes('next')) {
|
|
885
|
-
calendarPageInfo.year = YEAR_OF_NEXT_MONTH;
|
|
886
|
-
calendarPageInfo.month = NEXT_MONTH;
|
|
887
|
-
}
|
|
888
|
-
};
|
|
889
|
-
|
|
890
|
-
const setRangeModeDateByIndex = (currIndex, currDate) => {
|
|
891
|
-
if (!disabledDate
|
|
892
|
-
&& compareFromAndToDateTime(props.mode, calendarType, currDate, selectedValue.value)) {
|
|
893
|
-
return;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
selectedValue.value[currIndex] = currDate;
|
|
897
|
-
moveDispCalendarMonth();
|
|
898
|
-
updateCalendarPage(selectedValue.value);
|
|
899
|
-
};
|
|
900
|
-
|
|
901
|
-
switch (props.mode) {
|
|
902
|
-
case 'date':
|
|
903
|
-
selectedValue.value = CURR_DATE_STR;
|
|
904
|
-
moveDispCalendarMonth();
|
|
905
|
-
emit('update:modelValue', CURR_DATE_STR);
|
|
906
|
-
setCalendarDate('main');
|
|
907
|
-
break;
|
|
908
|
-
case 'dateTime': {
|
|
909
|
-
const isExistTime = !!(selectedValue.value?.split(' ')[1]);
|
|
910
|
-
const CURR_TIME_HMS = isExistTime
|
|
911
|
-
? selectedValue.value?.split(' ')[1] : '00:00:00';
|
|
912
|
-
selectedValue.value = getChangedValueByTimeFormat(
|
|
913
|
-
timeFormat,
|
|
914
|
-
`${CURR_DATE_STR} ${CURR_TIME_HMS}`,
|
|
915
|
-
);
|
|
916
|
-
moveDispCalendarMonth();
|
|
917
|
-
emit('update:modelValue', selectedValue.value);
|
|
918
|
-
setCalendarDate('main');
|
|
919
|
-
if (!isExistTime) {
|
|
920
|
-
const currTime = selectedValue.value.split(' ')[1].split(':');
|
|
921
|
-
setCalendarPageInfo('main', {
|
|
922
|
-
hour: Math.floor(currTime[0] / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
923
|
-
min: Math.floor(currTime[1] / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
924
|
-
sec: Math.floor(currTime[2] / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
925
|
-
});
|
|
926
|
-
setHmsTime();
|
|
927
|
-
}
|
|
928
|
-
break;
|
|
929
|
-
}
|
|
930
|
-
case 'dateMulti': {
|
|
931
|
-
const multiType = props.options.multiType;
|
|
932
|
-
const multiDayLimit = props.options.multiDayLimit;
|
|
933
|
-
if (multiType === 'date') {
|
|
934
|
-
const selectedIdx = selectedValue.value.indexOf(CURR_DATE_STR);
|
|
935
|
-
if (selectedIdx > -1) {
|
|
936
|
-
selectedValue.value.splice(selectedIdx, 1);
|
|
937
|
-
emit('update:modelValue', [...selectedValue.value]);
|
|
938
|
-
} else if (selectedValue.value.length < multiDayLimit) {
|
|
939
|
-
selectedValue.value.push(CURR_DATE_STR);
|
|
940
|
-
moveDispCalendarMonth();
|
|
941
|
-
emit('update:modelValue', [...selectedValue.value]);
|
|
942
|
-
}
|
|
943
|
-
} else if (multiType === 'week' || multiType === 'weekday') {
|
|
944
|
-
const NUMBER_OF_DAYS_IN_RANGE = multiType === 'week' ? 7 : 5; // 범위 내 선택된 날짜 개수
|
|
945
|
-
const DIFF_UNTIL_THE_LAST_DATE = multiType === 'week' ? 6 : 5; // 한 주의 마지막 날짜까지의 차이
|
|
946
|
-
const exactSelectedDate = new Date(`${CURR_DATE_STR} 00:00:00`);
|
|
947
|
-
const dayOfTheWeekOfTheSelectedDate = exactSelectedDate.getDay();
|
|
948
|
-
const diffFromTheLastDay = DIFF_UNTIL_THE_LAST_DATE - dayOfTheWeekOfTheSelectedDate;
|
|
949
|
-
const theLastDayTime = exactSelectedDate.getTime() + (ONE_DAY_MS * diffFromTheLastDay);
|
|
950
|
-
|
|
951
|
-
for (let i = 0; i < NUMBER_OF_DAYS_IN_RANGE; i++) {
|
|
952
|
-
const loopYear = new Date(theLastDayTime - (i * ONE_DAY_MS)).getFullYear();
|
|
953
|
-
const loopMonth = new Date(theLastDayTime - (i * ONE_DAY_MS)).getMonth() + 1;
|
|
954
|
-
const loopDate = new Date(theLastDayTime - (i * ONE_DAY_MS)).getDate();
|
|
955
|
-
const dateStr = `${loopYear}-${lpadToTwoDigits(loopMonth)}-${lpadToTwoDigits(loopDate)}`;
|
|
956
|
-
if (i === 0) {
|
|
957
|
-
if (selectedValue.value.includes(dateStr)) {
|
|
958
|
-
selectedValue.value.splice(0);
|
|
959
|
-
break;
|
|
960
|
-
} else {
|
|
961
|
-
selectedValue.value.splice(0);
|
|
962
|
-
moveDispCalendarMonth();
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
if (!disabledDate || !disabledDate(new Date(dateStr))) {
|
|
966
|
-
selectedValue.value.unshift(dateStr);
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
emit('update:modelValue', [...selectedValue.value]);
|
|
970
|
-
}
|
|
971
|
-
setCalendarDate('main');
|
|
972
|
-
break;
|
|
973
|
-
}
|
|
974
|
-
case 'dateRange': {
|
|
975
|
-
if (!selectedValue.value.length) {
|
|
976
|
-
selectedValue.value.push(CURR_DATE_STR);
|
|
977
|
-
selectedValue.value.push(CURR_DATE_STR);
|
|
978
|
-
updateCalendarPage(selectedValue.value);
|
|
979
|
-
} else {
|
|
980
|
-
setRangeModeDateByIndex(calendarType !== 'main' | 0, CURR_DATE_STR);
|
|
981
|
-
}
|
|
982
|
-
emit('update:modelValue', [...selectedValue.value]);
|
|
983
|
-
break;
|
|
984
|
-
}
|
|
985
|
-
case 'dateTimeRange': {
|
|
986
|
-
if (!selectedValue.value.length) {
|
|
987
|
-
let fromDate = `${CURR_DATE_STR} 00:00:00`;
|
|
988
|
-
let toDate = `${CURR_DATE_STR} 00:00:00`;
|
|
989
|
-
if (timeFormat && timeFormat.length) {
|
|
990
|
-
fromDate = getChangedValueByTimeFormat(
|
|
991
|
-
timeFormat[0],
|
|
992
|
-
fromDate,
|
|
993
|
-
);
|
|
994
|
-
toDate = getChangedValueByTimeFormat(
|
|
995
|
-
timeFormat[1],
|
|
996
|
-
toDate,
|
|
997
|
-
);
|
|
998
|
-
}
|
|
999
|
-
selectedValue.value.push(fromDate);
|
|
1000
|
-
selectedValue.value.push(toDate);
|
|
1001
|
-
|
|
1002
|
-
updateCalendarPage(selectedValue.value);
|
|
1003
|
-
setHmsTime();
|
|
1004
|
-
} else {
|
|
1005
|
-
const currIndex = calendarType !== 'main' | 0;
|
|
1006
|
-
const CURR_TIME_HMS = selectedValue.value[currIndex]?.split(' ')[1] || '00:00:00';
|
|
1007
|
-
|
|
1008
|
-
let currDate = `${CURR_DATE_STR} ${CURR_TIME_HMS}`;
|
|
1009
|
-
if (timeFormat && timeFormat.length) {
|
|
1010
|
-
currDate = getChangedValueByTimeFormat(
|
|
1011
|
-
timeFormat[currIndex],
|
|
1012
|
-
currDate,
|
|
1013
|
-
);
|
|
1014
|
-
}
|
|
1015
|
-
setRangeModeDateByIndex(currIndex, currDate);
|
|
1016
|
-
}
|
|
1017
|
-
emit('update:modelValue', [...selectedValue.value]);
|
|
1018
|
-
break;
|
|
1019
|
-
}
|
|
1020
|
-
default:
|
|
1021
|
-
break;
|
|
1022
|
-
}
|
|
1023
|
-
};
|
|
1024
|
-
|
|
1025
|
-
/**
|
|
1026
|
-
* Calendar mode: dateTime인 경우 HMS 이동 화살표 클릭 이벤트
|
|
1027
|
-
* @param calendarType - {main|expanded}
|
|
1028
|
-
* @param timeType - {hour|min|sec}
|
|
1029
|
-
* @param arrow - {up|down}
|
|
1030
|
-
*/
|
|
1031
|
-
const clickHmsBtn = (calendarType, timeType, arrow) => {
|
|
1032
|
-
if (preventTimeEventType[calendarType][timeType]) {
|
|
1033
|
-
return;
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
const calendarPageInfo = calendarType === 'expanded'
|
|
1037
|
-
? expandedCalendarPageInfo : mainCalendarPageInfo;
|
|
1038
|
-
const FIRST_PAGE = 1;
|
|
1039
|
-
const HOUR_MAX_PAGE = 2;
|
|
1040
|
-
const MINUTE_MAX_PAGE = 5;
|
|
1041
|
-
const SECOND_MAX_PAGE = 5;
|
|
1042
|
-
if (timeType === 'hour') {
|
|
1043
|
-
if (arrow === 'down' && calendarPageInfo.hour < HOUR_MAX_PAGE) {
|
|
1044
|
-
calendarPageInfo.hour++;
|
|
1045
|
-
} else if (arrow === 'up' && calendarPageInfo.hour > FIRST_PAGE) {
|
|
1046
|
-
calendarPageInfo.hour--;
|
|
1047
|
-
}
|
|
1048
|
-
} else if (timeType === 'min') {
|
|
1049
|
-
if (arrow === 'down' && calendarPageInfo.min < MINUTE_MAX_PAGE) {
|
|
1050
|
-
calendarPageInfo.min++;
|
|
1051
|
-
} else if (arrow === 'up' && calendarPageInfo.min > FIRST_PAGE) {
|
|
1052
|
-
calendarPageInfo.min--;
|
|
1053
|
-
}
|
|
1054
|
-
} else if (timeType === 'sec') {
|
|
1055
|
-
if (arrow === 'down' && calendarPageInfo.sec < SECOND_MAX_PAGE) {
|
|
1056
|
-
calendarPageInfo.sec++;
|
|
1057
|
-
} else if (arrow === 'up' && calendarPageInfo.sec > FIRST_PAGE) {
|
|
1058
|
-
calendarPageInfo.sec--;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
};
|
|
1062
|
-
|
|
1063
|
-
/**
|
|
1064
|
-
* Click cell In HMS area
|
|
1065
|
-
* @param calendarType - {main|expanded}
|
|
1066
|
-
* @param timeType - {hour|min|sec}
|
|
1067
|
-
* @param i - row
|
|
1068
|
-
* @param j - col
|
|
1069
|
-
*/
|
|
1070
|
-
const clickTime = (calendarType, timeType, i, j) => {
|
|
1071
|
-
if (preventTimeEventType[calendarType][timeType]) {
|
|
1072
|
-
return;
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
const calendarPageInfo = calendarType === 'expanded'
|
|
1076
|
-
? expandedCalendarPageInfo : mainCalendarPageInfo;
|
|
1077
|
-
const timeInfo = calendarType === 'main'
|
|
1078
|
-
? mainTimeTableInfo : expandedTimeTableInfo;
|
|
1079
|
-
const currPage = calendarPageInfo[timeType] - 1;
|
|
1080
|
-
const currRowIdx = i - 1;
|
|
1081
|
-
const currColIdx = j - 1;
|
|
1082
|
-
const clickedNum = (currPage * CELL_CNT_IN_ONE_PAGE)
|
|
1083
|
-
+ (currRowIdx * CELL_CNT_IN_ONE_ROW) + currColIdx;
|
|
1084
|
-
|
|
1085
|
-
if (timeInfo[timeType][clickedNum]?.isDisabled) {
|
|
1086
|
-
return;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
const TODAY = new Date();
|
|
1090
|
-
const TODAY_INFO = {
|
|
1091
|
-
year: TODAY.getFullYear(),
|
|
1092
|
-
month: TODAY.getMonth() + 1,
|
|
1093
|
-
date: TODAY.getDate(),
|
|
1094
|
-
};
|
|
1095
|
-
let EXIST_MODEL = true;
|
|
1096
|
-
let valueListByUpdatePage = [];
|
|
1097
|
-
|
|
1098
|
-
const getTimeValueByType = () => {
|
|
1099
|
-
let targetTimeValue;
|
|
1100
|
-
if (timeType === 'hour') {
|
|
1101
|
-
targetTimeValue = `${lpadToTwoDigits(clickedNum)}:00:00'`;
|
|
1102
|
-
} else if (timeType === 'min') {
|
|
1103
|
-
targetTimeValue = `00:${lpadToTwoDigits(clickedNum)}:00`;
|
|
1104
|
-
} else if (timeType === 'sec') {
|
|
1105
|
-
targetTimeValue = `00:00:${lpadToTwoDigits(clickedNum)}`;
|
|
1106
|
-
}
|
|
1107
|
-
return `${formatDateTime(TODAY_INFO)} ${targetTimeValue}`;
|
|
1108
|
-
};
|
|
1109
|
-
|
|
1110
|
-
const getChangedValue = (targetValue) => {
|
|
1111
|
-
const HOUR_START_IDX = 11;
|
|
1112
|
-
const MIN_START_IDX = 14;
|
|
1113
|
-
const SEC_START_IDX = 17;
|
|
1114
|
-
const REPLACE_TEXT_SIZE = 2;
|
|
1115
|
-
let START_IDX = HOUR_START_IDX;
|
|
1116
|
-
if (timeType === 'min') {
|
|
1117
|
-
START_IDX = MIN_START_IDX;
|
|
1118
|
-
} else if (timeType === 'sec') {
|
|
1119
|
-
START_IDX = SEC_START_IDX;
|
|
1120
|
-
}
|
|
1121
|
-
return `${targetValue?.substr(0, START_IDX)}`
|
|
1122
|
-
+ `${lpadToTwoDigits(clickedNum)}${targetValue?.substr(START_IDX + REPLACE_TEXT_SIZE)}`;
|
|
1123
|
-
};
|
|
1124
|
-
|
|
1125
|
-
if (props.mode === 'dateTime') {
|
|
1126
|
-
if (!props.modelValue) {
|
|
1127
|
-
EXIST_MODEL = false;
|
|
1128
|
-
selectedValue.value = getChangedValueByTimeFormat(
|
|
1129
|
-
timeFormat,
|
|
1130
|
-
getTimeValueByType(),
|
|
1131
|
-
);
|
|
1132
|
-
emit('update:modelValue', selectedValue.value);
|
|
1133
|
-
} else {
|
|
1134
|
-
selectedValue.value = getChangedValueByTimeFormat(
|
|
1135
|
-
timeFormat,
|
|
1136
|
-
getChangedValue(props.modelValue),
|
|
1137
|
-
);
|
|
1138
|
-
emit('update:modelValue', selectedValue.value);
|
|
1139
|
-
}
|
|
1140
|
-
valueListByUpdatePage.push(selectedValue.value);
|
|
1141
|
-
} else {
|
|
1142
|
-
const index = calendarType !== 'main' | 0;
|
|
1143
|
-
if (!props.modelValue.length) {
|
|
1144
|
-
const timeValue = getTimeValueByType();
|
|
1145
|
-
selectedValue.value = [timeValue, timeValue];
|
|
1146
|
-
|
|
1147
|
-
if (timeFormat && timeFormat.length) {
|
|
1148
|
-
selectedValue.value = [...selectedValue.value
|
|
1149
|
-
.map((v, idx) => getChangedValueByTimeFormat(timeFormat[idx], v))];
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
EXIST_MODEL = false;
|
|
1153
|
-
valueListByUpdatePage = selectedValue.value;
|
|
1154
|
-
} else {
|
|
1155
|
-
let currDateTime = getChangedValue(props.modelValue[index]);
|
|
1156
|
-
if (timeFormat && timeFormat.length) {
|
|
1157
|
-
currDateTime = getChangedValueByTimeFormat(
|
|
1158
|
-
timeFormat[index],
|
|
1159
|
-
currDateTime,
|
|
1160
|
-
);
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
selectedValue.value[index] = currDateTime;
|
|
1164
|
-
}
|
|
1165
|
-
emit('update:modelValue', [...selectedValue.value]);
|
|
1166
|
-
}
|
|
1167
|
-
setHmsTime();
|
|
1168
|
-
// dateTime의 v-model값이 없는 경우 time area를 클릭하였을 때 date의 값은 today로 세팅
|
|
1169
|
-
if (!EXIST_MODEL) {
|
|
1170
|
-
updateCalendarPage(valueListByUpdatePage);
|
|
1171
|
-
}
|
|
1172
|
-
};
|
|
1173
|
-
|
|
1174
|
-
/**
|
|
1175
|
-
* Wheel up or wheel down In Calendar Month(tbody) area
|
|
1176
|
-
* @param calendarType - {main|expanded}
|
|
1177
|
-
* @param e
|
|
1178
|
-
*/
|
|
1179
|
-
const wheelMonth = (calendarType, e) => {
|
|
1180
|
-
moveMonth(calendarType, e.deltaY > 0 ? 'next' : 'prev');
|
|
1181
|
-
setCalendarDate(calendarType);
|
|
1182
|
-
};
|
|
1183
|
-
|
|
1184
|
-
/**
|
|
1185
|
-
* Wheel up or wheel down In Calendar Time(HMS) area
|
|
1186
|
-
* @param calendarType - {main|expanded}
|
|
1187
|
-
* @param timeType - {hour|min|sec}
|
|
1188
|
-
* @param e
|
|
1189
|
-
*/
|
|
1190
|
-
const wheelTime = (calendarType, timeType, e) => {
|
|
1191
|
-
if (preventTimeEventType[calendarType][timeType]) {
|
|
1192
|
-
return;
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
clickHmsBtn(calendarType, timeType, e.deltaY > 0 ? 'down' : 'up');
|
|
1196
|
-
};
|
|
1197
|
-
|
|
1198
|
-
/**
|
|
1199
|
-
* dateRange 모드에서 한번 클릭 후 날짜에 마우스무브하는 경우
|
|
1200
|
-
* 커서 위에 있는 날짜까지의 selectedValue의 영역 선택한 selection 로직
|
|
1201
|
-
* 일반적인 마우스무브 로직에 성능향상을 위한 throttle 10ms를 설정
|
|
1202
|
-
* @param calendarType - 캘린더 종류 ('main'|'expanded')
|
|
1203
|
-
* @param e - 마우스이벤트
|
|
1204
|
-
* @type {function(): (*)}
|
|
1205
|
-
*/
|
|
1206
|
-
const onMousemoveDate = throttle((calendarType, e) => {
|
|
1207
|
-
const target = e.target.tagName === 'TD' ? e.target : e.target.parentElement;
|
|
1208
|
-
const isDisabled = target.classList.contains('disabled');
|
|
1209
|
-
const isPrev = target.classList.contains('prev');
|
|
1210
|
-
const isNext = target.classList.contains('next');
|
|
1211
|
-
if (target.classList.length > 0 && !isDisabled) {
|
|
1212
|
-
const calendarPageInfo = calendarType === 'main' ? mainCalendarPageInfo : expandedCalendarPageInfo;
|
|
1213
|
-
let yearMonth = {
|
|
1214
|
-
year: +calendarPageInfo.year,
|
|
1215
|
-
month: +calendarPageInfo.month,
|
|
1216
|
-
date: e.target.innerText,
|
|
1217
|
-
};
|
|
1218
|
-
// 달력 내 이전달, 다음달 일자의 경우 연, 월 보정
|
|
1219
|
-
if (isPrev) {
|
|
1220
|
-
yearMonth = { ...yearMonth, ...getSideMonthCalendarInfo('prev', yearMonth.year, yearMonth.month) };
|
|
1221
|
-
} else if (isNext) {
|
|
1222
|
-
yearMonth = { ...yearMonth, ...getSideMonthCalendarInfo('next', yearMonth.year, yearMonth.month) };
|
|
1223
|
-
}
|
|
1224
|
-
const STANDARD_DATE_STR = dateRangeClickedDate.value;
|
|
1225
|
-
const MOUSEMOVE_DATE_STR = formatDateTime({
|
|
1226
|
-
year: yearMonth.year,
|
|
1227
|
-
month: yearMonth.month,
|
|
1228
|
-
date: yearMonth.date,
|
|
1229
|
-
});
|
|
1230
|
-
|
|
1231
|
-
// fromDate ~ toDate selection 순서 세팅
|
|
1232
|
-
if (getDateMs(MOUSEMOVE_DATE_STR) < getDateMs(STANDARD_DATE_STR)) {
|
|
1233
|
-
selectedValue.value[0] = MOUSEMOVE_DATE_STR;
|
|
1234
|
-
selectedValue.value[1] = STANDARD_DATE_STR;
|
|
1235
|
-
} else {
|
|
1236
|
-
selectedValue.value[0] = STANDARD_DATE_STR;
|
|
1237
|
-
selectedValue.value[1] = MOUSEMOVE_DATE_STR;
|
|
1238
|
-
}
|
|
1239
|
-
setCalendarDate('main');
|
|
1240
|
-
setCalendarDate('expanded');
|
|
1241
|
-
}
|
|
1242
|
-
}, 10);
|
|
1243
|
-
|
|
1244
|
-
watch(
|
|
1245
|
-
() => props.modelValue,
|
|
1246
|
-
(curr) => {
|
|
1247
|
-
selectedValue.value = curr;
|
|
1248
|
-
|
|
1249
|
-
if (props.mode.includes('Time')) {
|
|
1250
|
-
let updateValue = [];
|
|
1251
|
-
if (props.mode === 'dateTime') {
|
|
1252
|
-
updateValue = [selectedValue.value];
|
|
1253
|
-
} else if (props.mode === 'dateTimeRange') {
|
|
1254
|
-
updateValue = selectedValue.value;
|
|
1255
|
-
}
|
|
1256
|
-
updateCalendarPage(updateValue);
|
|
1257
|
-
setHmsTime();
|
|
1258
|
-
}
|
|
1259
|
-
});
|
|
1260
|
-
|
|
1261
|
-
return {
|
|
1262
|
-
clickPrevNextBtn,
|
|
1263
|
-
clickDate,
|
|
1264
|
-
clickHmsBtn,
|
|
1265
|
-
clickTime,
|
|
1266
|
-
wheelMonth,
|
|
1267
|
-
wheelTime,
|
|
1268
|
-
calendarEventName,
|
|
1269
|
-
onMousemoveDate,
|
|
1270
|
-
preventTimeEventType,
|
|
1271
|
-
};
|
|
1272
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
ref, reactive, computed, getCurrentInstance, unref, onBeforeMount, watch,
|
|
3
|
+
} from 'vue';
|
|
4
|
+
import { throttle } from 'lodash-es';
|
|
5
|
+
|
|
6
|
+
const CALENDAR_ROWS = 6;
|
|
7
|
+
const CALENDAR_COLS = 7;
|
|
8
|
+
const MONTH_CNT = 12;
|
|
9
|
+
const HOUR_CNT = 24;
|
|
10
|
+
const MIN_CNT = 60;
|
|
11
|
+
const SEC_CNT = 60;
|
|
12
|
+
const CELL_CNT_IN_ONE_PAGE = 12;
|
|
13
|
+
const CELL_CNT_IN_ONE_ROW = 4;
|
|
14
|
+
const MONTH_NAME_LIST = {
|
|
15
|
+
fullName: ['January', 'February', 'March', 'April', 'May', 'June',
|
|
16
|
+
'July', 'August', 'September', 'October', 'November', 'December'],
|
|
17
|
+
numberName: ['1', '2', '3', '4', '5', '6',
|
|
18
|
+
'7', '8', '9', '10', '11', '12'],
|
|
19
|
+
abbrName: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
|
20
|
+
'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'],
|
|
21
|
+
korName: ['1월', '2월', '3월', '4월', '5월', '6월',
|
|
22
|
+
'7월', '8월', '9월', '10월', '11월', '12월'],
|
|
23
|
+
};
|
|
24
|
+
const DAY_OF_THE_WEEK_NAME_LIST = {
|
|
25
|
+
abbrUpperName: ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'],
|
|
26
|
+
abbrLowerName: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
|
|
27
|
+
abbrPascalName: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
28
|
+
abbrKorName: ['일', '월', '화', '수', '목', '금', '토'],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const ONE_DAY_MS = 86400000;
|
|
32
|
+
const dateReg = new RegExp(/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/);
|
|
33
|
+
const dateTimeReg = new RegExp(/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9]/);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 배열 내 여러 날짜(eg. 'YYYY-MM-DD' || 'YYYY-MM-DD HH:MI:SS') 중 가장 끝의 날짜 텍스트 구하기
|
|
37
|
+
* @param arr
|
|
38
|
+
* @param sideDirection - 끝의 방향 (first: 가장 멀리 오래된 날짜, last: 가장 최근의 날짜)
|
|
39
|
+
* @returns {String} - 날짜 텍스트
|
|
40
|
+
*/
|
|
41
|
+
const getSideDateStr = (arr, sideDirection) => {
|
|
42
|
+
if (!arr.length) return '';
|
|
43
|
+
if (sideDirection === 'last') {
|
|
44
|
+
return arr
|
|
45
|
+
.reduce((prev, cur) => (new Date(prev).getTime() > new Date(cur).getTime() ? prev : cur));
|
|
46
|
+
}
|
|
47
|
+
return arr
|
|
48
|
+
.reduce((prev, cur) => (new Date(prev).getTime() < new Date(cur).getTime() ? prev : cur));
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 월, 일을 두자리 숫자로 보정
|
|
53
|
+
* @param num
|
|
54
|
+
* @returns {string|*}
|
|
55
|
+
*/
|
|
56
|
+
export const lpadToTwoDigits = (num) => {
|
|
57
|
+
if (num === null) {
|
|
58
|
+
return '00';
|
|
59
|
+
} else if (+num < 10) {
|
|
60
|
+
return `0${num}`;
|
|
61
|
+
}
|
|
62
|
+
return num;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 이차원 배열 만들기
|
|
67
|
+
* @param row
|
|
68
|
+
* @param col
|
|
69
|
+
* @returns {Array} - [row][col]
|
|
70
|
+
*/
|
|
71
|
+
const getMatrixArr = (row, col) => Array.from(Array(row), () => Array(col).fill(false));
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* y년 m월 1일의 요일 구하기
|
|
75
|
+
* @param y - 년
|
|
76
|
+
* @param m - 월
|
|
77
|
+
* @returns {number} - 해당 y년 m월 1일의 요일 (e.g. 0: SUN, ..., 6: SAT)
|
|
78
|
+
* - 1주차에서 일요일부터 1일까지의 공백 개수
|
|
79
|
+
*/
|
|
80
|
+
const getDayOfWeekOnThe1stOfMonth = (y, m) => new Date(`${y}-${m}-1`).getDay();
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* y년 m월 마지막 일자 구하기
|
|
84
|
+
* @param y
|
|
85
|
+
* @param m
|
|
86
|
+
* @returns {number} - 해당 년, 월의 마지막 일자
|
|
87
|
+
*/
|
|
88
|
+
const getLastDateOfMonth = (y, m) => {
|
|
89
|
+
let day;
|
|
90
|
+
switch (m) {
|
|
91
|
+
case 4:
|
|
92
|
+
case 6:
|
|
93
|
+
case 9:
|
|
94
|
+
case 11:
|
|
95
|
+
day = 30;
|
|
96
|
+
break;
|
|
97
|
+
case 2:
|
|
98
|
+
if (((y % 4 === 0) && (y % 100 !== 0)) || (y % 400 === 0)) {
|
|
99
|
+
day = 29;
|
|
100
|
+
} else {
|
|
101
|
+
day = 28;
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
default:
|
|
105
|
+
day = 31;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
return day;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* date또는 time 형태로 format string으로 조합
|
|
113
|
+
* @param year
|
|
114
|
+
* @param month
|
|
115
|
+
* @param date
|
|
116
|
+
* @param hour
|
|
117
|
+
* @param min
|
|
118
|
+
* @param sec
|
|
119
|
+
* @returns {string}
|
|
120
|
+
*/
|
|
121
|
+
const formatDateTime = ({ year, month, date, hour, min, sec }) => {
|
|
122
|
+
if (hour !== undefined && min !== undefined && sec !== undefined) {
|
|
123
|
+
return `${year}-${lpadToTwoDigits(month)}-${lpadToTwoDigits(date)} ${lpadToTwoDigits(hour)}:${lpadToTwoDigits(min)}:${lpadToTwoDigits(sec)}`;
|
|
124
|
+
}
|
|
125
|
+
return `${year}-${lpadToTwoDigits(month)}-${lpadToTwoDigits(date)}`;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 첫번째 인자로 받은 날짜 형식 String ('YYYY-MM-DD' || 'YYYY-MM-DD HH:MI:SS')이나
|
|
130
|
+
* 해당 날짜형식이 들어있는 Array를 받아서 최신날짜의 정보를 추출하는 함수
|
|
131
|
+
* typeToImport가 존재하는 경우 해당 timeType의 값을
|
|
132
|
+
* typeToImport가 존재하지 않는 경우 최신날짜 텍스트를 timeType별로 분할한 Object를 리턴
|
|
133
|
+
* @param param {String | Array} - 변경하려는 날짜
|
|
134
|
+
* @param typeToImport
|
|
135
|
+
* @returns {object|number}
|
|
136
|
+
*/
|
|
137
|
+
const getDateTimeInfoByType = (param, typeToImport) => {
|
|
138
|
+
let str = unref(param);
|
|
139
|
+
if (Array.isArray(str)) {
|
|
140
|
+
str = getSideDateStr(param, 'last');
|
|
141
|
+
}
|
|
142
|
+
const result = {
|
|
143
|
+
year: +(str?.split(' ')[0]?.split('-')[0]) || null,
|
|
144
|
+
month: +(str?.split(' ')[0]?.split('-')[1]) || null,
|
|
145
|
+
date: +(str?.split(' ')[0]?.split('-')[2]) || null,
|
|
146
|
+
hour: +(str?.split(' ')[1]?.split(':')[0]) || 0,
|
|
147
|
+
min: +(str?.split(' ')[1]?.split(':')[1]) || 0,
|
|
148
|
+
sec: +(str?.split(' ')[1]?.split(':')[2]) || 0,
|
|
149
|
+
};
|
|
150
|
+
if (typeToImport === 'year') return result.year;
|
|
151
|
+
if (typeToImport === 'month') return result.month;
|
|
152
|
+
if (typeToImport === 'date') return result.date;
|
|
153
|
+
if (typeToImport === 'hour') return result.hour;
|
|
154
|
+
if (typeToImport === 'min') return result.min;
|
|
155
|
+
if (typeToImport === 'sec') return result.sec;
|
|
156
|
+
return result;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* 이전달, 다음달의 달력 상 연도, 월 정보 구하기
|
|
160
|
+
* @param prevNext - 이전, 다음 여부 ('prev'|'next')
|
|
161
|
+
* @param year
|
|
162
|
+
* @param month
|
|
163
|
+
* @returns {{month: number, year: *}}
|
|
164
|
+
*/
|
|
165
|
+
const getSideMonthCalendarInfo = (prevNext, year, month) => {
|
|
166
|
+
if (prevNext === 'next') {
|
|
167
|
+
return {
|
|
168
|
+
year: month === 12 ? year + 1 : year,
|
|
169
|
+
month: ((month + 1) % 12) || 12,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
year: month === 1 ? year - 1 : year,
|
|
174
|
+
month: ((month - 1) % 12) || 12,
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* timeFormat을 체크하여 timeFormat이 있으면 format에 맞는 형식으로 반환
|
|
180
|
+
* @param timeFormat -- props.option?.timeFormat
|
|
181
|
+
* @param dateTimeValue
|
|
182
|
+
* @param typeToImport
|
|
183
|
+
* @returns {Object|number}
|
|
184
|
+
*/
|
|
185
|
+
const getTimeInfoByTimeFormat = (timeFormat, dateTimeValue, typeToImport) => {
|
|
186
|
+
const value = getDateTimeInfoByType(dateTimeValue, typeToImport);
|
|
187
|
+
if (timeFormat) {
|
|
188
|
+
const hour = timeFormat?.split(':')[0];
|
|
189
|
+
const min = timeFormat?.split(':')[1];
|
|
190
|
+
const sec = timeFormat?.split(':')[2];
|
|
191
|
+
if (typeToImport === 'hour') {
|
|
192
|
+
return hour === 'HH' ? value : +hour;
|
|
193
|
+
} else if (typeToImport === 'min') {
|
|
194
|
+
return min === 'mm' ? value : +min;
|
|
195
|
+
} else if (typeToImport === 'sec') {
|
|
196
|
+
return sec === 'ss' ? value : +sec;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return value;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 초기 timeFormat에 따른 modelValue update 함수
|
|
204
|
+
* @param timeFormat - props.options.timeFormat
|
|
205
|
+
* @param modelValue
|
|
206
|
+
* @returns string
|
|
207
|
+
*/
|
|
208
|
+
export const getChangedValueByTimeFormat = (timeFormat, modelValue) => {
|
|
209
|
+
if (!modelValue) {
|
|
210
|
+
return '';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const hourByTimeFormat = lpadToTwoDigits(getTimeInfoByTimeFormat(timeFormat, modelValue, 'hour'));
|
|
214
|
+
const minByTimeFormat = lpadToTwoDigits(getTimeInfoByTimeFormat(timeFormat, modelValue, 'min'));
|
|
215
|
+
const secByTimeFormat = lpadToTwoDigits(getTimeInfoByTimeFormat(timeFormat, modelValue, 'sec'));
|
|
216
|
+
|
|
217
|
+
return `${modelValue.split(' ')[0]} ${hourByTimeFormat}:${minByTimeFormat}:${secByTimeFormat}`;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const compareFromAndToDateTime = (mode, calendarType, targetDate, modelValue) => {
|
|
221
|
+
if (!modelValue.length) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
let fromDate = calendarType === 'main' ? targetDate : modelValue[0];
|
|
225
|
+
let toDate = calendarType === 'expanded' ? targetDate : modelValue[1];
|
|
226
|
+
|
|
227
|
+
let fromDateTime = fromDate;
|
|
228
|
+
let toDateTime = toDate;
|
|
229
|
+
if (!targetDate.split(' ')[1]) {
|
|
230
|
+
if (mode === 'dateTimeRange') {
|
|
231
|
+
fromDate = fromDate.split(' ')[0];
|
|
232
|
+
toDate = toDate.split(' ')[0];
|
|
233
|
+
const fromTime = modelValue[0].split(' ')[1];
|
|
234
|
+
const toTime = modelValue[1].split(' ')[1];
|
|
235
|
+
fromDateTime = `${fromDate} ${fromTime}`;
|
|
236
|
+
toDateTime = `${toDate} ${toTime}`;
|
|
237
|
+
} else {
|
|
238
|
+
fromDateTime = `${fromDate} 00:00:00`;
|
|
239
|
+
toDateTime = `${toDate} 23:59:59`;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return (fromDateTime && toDateTime)
|
|
244
|
+
&& new Date(fromDateTime).getTime() > +new Date(toDateTime).getTime();
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* date string 값의 MS 값 구하기
|
|
249
|
+
* @param dateStr
|
|
250
|
+
* @returns {number}
|
|
251
|
+
*/
|
|
252
|
+
const getDateMs = dateStr => new Date(`${dateStr}`).getTime();
|
|
253
|
+
|
|
254
|
+
export const useModel = () => {
|
|
255
|
+
const { props } = getCurrentInstance();
|
|
256
|
+
const timeFormat = props.options?.timeFormat;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 현재 선택된 값, 배열인 경우 반응형을 끊기위해 rest 사용
|
|
260
|
+
* selectValue ref로 변환하기 전 modelValue timeFormat에 따라 fetch
|
|
261
|
+
* 1) props.mode: 'date' or 'dateTime' > String
|
|
262
|
+
* 2) props.mode: 'dateMulti' or 'dateRange' > [...Array]
|
|
263
|
+
*/
|
|
264
|
+
let selectedValue;
|
|
265
|
+
if (props.mode !== 'dateMulti' && props.mode !== 'dateRange' && props.mode !== 'dateTimeRange') {
|
|
266
|
+
if (props.modelValue
|
|
267
|
+
&& ((props.modelValue.length === 10 && dateReg.exec(props.modelValue?.toString()))
|
|
268
|
+
|| (props.modelValue.length === 19 && dateTimeReg.exec(props.modelValue?.toString())))
|
|
269
|
+
) {
|
|
270
|
+
if (props.mode === 'dateTime' && timeFormat) {
|
|
271
|
+
const modelValue = getChangedValueByTimeFormat(timeFormat, props.modelValue);
|
|
272
|
+
selectedValue = ref(modelValue);
|
|
273
|
+
} else {
|
|
274
|
+
selectedValue = ref(props.modelValue);
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
selectedValue = ref('');
|
|
278
|
+
}
|
|
279
|
+
} else if (Array.isArray(props.modelValue)
|
|
280
|
+
&& props.modelValue.every(v => (
|
|
281
|
+
!v
|
|
282
|
+
|| (v.length === 10 && dateReg.exec(v))
|
|
283
|
+
|| (v.length === 19 && dateTimeReg.exec(v))
|
|
284
|
+
))
|
|
285
|
+
) {
|
|
286
|
+
if (props.mode === 'dateTimeRange' && props.modelValue.length === 2 && timeFormat) {
|
|
287
|
+
const modelValue = [];
|
|
288
|
+
modelValue.push(getChangedValueByTimeFormat(timeFormat[0], props.modelValue[0]));
|
|
289
|
+
modelValue.push(getChangedValueByTimeFormat(timeFormat[1], props.modelValue[1]));
|
|
290
|
+
selectedValue = ref([...modelValue]);
|
|
291
|
+
} else {
|
|
292
|
+
selectedValue = ref([...props.modelValue]);
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
selectedValue = ref([]);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* validate v-model's value
|
|
300
|
+
*/
|
|
301
|
+
const validateModelValue = () => {
|
|
302
|
+
if (props.mode === 'date' && props.modelValue && typeof props.modelValue !== 'string') {
|
|
303
|
+
console.warn('[EVUI][Calendar] When mode is \'date\', v-model must be \'String\' type.');
|
|
304
|
+
} else if (props.mode === 'dateTime' && props.modelValue && typeof props.modelValue !== 'string') {
|
|
305
|
+
console.warn('[EVUI][Calendar] When mode is \'dateTime\', v-model must be \'String\' type.');
|
|
306
|
+
} else if (props.mode === 'dateMulti' && props.modelValue && !Array.isArray(props.modelValue)) {
|
|
307
|
+
console.warn('[EVUI][Calendar] When mode is \'dateMulti\', v-model must be \'Array\' type.');
|
|
308
|
+
} else if (props.mode === 'dateRange' && props.modelValue) {
|
|
309
|
+
if (!Array.isArray(props.modelValue)) {
|
|
310
|
+
console.warn('[EVUI][Calendar] When mode is \'dateRange\', v-model must be \'Array\' type.');
|
|
311
|
+
} else if (getDateMs(`${props.modelValue[0]} 00:00:00`) > getDateMs(`${props.modelValue[1]} 00:00:00`)) {
|
|
312
|
+
console.warn('[EVUI][Calendar] When mode is \'dateRange\', fromDate must be less than toDate.');
|
|
313
|
+
}
|
|
314
|
+
} else if (props.mode === 'dateTimeRange' && props.modelValue) {
|
|
315
|
+
if (!Array.isArray(props.modelValue)) {
|
|
316
|
+
console.warn('[EVUI][Calendar] When mode is \'dateTimeRange\', v-model must be \'Array\' type.');
|
|
317
|
+
} else if (getDateMs(props.modelValue[0]) > getDateMs(props.modelValue[1])) {
|
|
318
|
+
console.warn('[EVUI][Calendar] When mode is \'dateRange\', fromDate must be less than toDate.');
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// 메인(좌측) 달력(연, 월, 시, 분, 초) 페이징 정보
|
|
324
|
+
let mainCalendarPageInfo;
|
|
325
|
+
const mainValue = !['dateRange', 'dateTimeRange'].includes(props.mode) ? selectedValue.value : selectedValue.value[0];
|
|
326
|
+
if (mainValue?.length) {
|
|
327
|
+
mainCalendarPageInfo = reactive({
|
|
328
|
+
year: getDateTimeInfoByType(mainValue, 'year'),
|
|
329
|
+
month: getDateTimeInfoByType(mainValue, 'month'),
|
|
330
|
+
hour: Math.floor(getDateTimeInfoByType(mainValue, 'hour') / CELL_CNT_IN_ONE_PAGE) + 1 || 1,
|
|
331
|
+
min: Math.floor(getDateTimeInfoByType(mainValue, 'min') / CELL_CNT_IN_ONE_PAGE) + 1 || 1,
|
|
332
|
+
sec: Math.floor(getDateTimeInfoByType(mainValue, 'sec') / CELL_CNT_IN_ONE_PAGE) + 1 || 1,
|
|
333
|
+
});
|
|
334
|
+
} else {
|
|
335
|
+
mainCalendarPageInfo = reactive({
|
|
336
|
+
year: new Date().getFullYear(),
|
|
337
|
+
month: new Date().getMonth() + 1,
|
|
338
|
+
hour: 1,
|
|
339
|
+
min: 1,
|
|
340
|
+
sec: 1,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// 'mode: dateRange || dateTimeRange', 인 경우 확장된 달력(연, 월) 페이징 정보
|
|
345
|
+
let expandedCalendarPageInfo;
|
|
346
|
+
if ((['dateRange', 'dateTimeRange'].includes(props.mode))
|
|
347
|
+
&& Array.isArray(selectedValue.value)
|
|
348
|
+
&& selectedValue.value[1]
|
|
349
|
+
) {
|
|
350
|
+
const expandedValue = selectedValue.value[1];
|
|
351
|
+
const toDate = {
|
|
352
|
+
year: getDateTimeInfoByType(expandedValue, 'year'),
|
|
353
|
+
month: getDateTimeInfoByType(expandedValue, 'month'),
|
|
354
|
+
};
|
|
355
|
+
expandedCalendarPageInfo = reactive(toDate);
|
|
356
|
+
|
|
357
|
+
if (props.mode === 'dateTimeRange') {
|
|
358
|
+
expandedCalendarPageInfo.hour = Math.floor(getDateTimeInfoByType(expandedValue, 'hour') / CELL_CNT_IN_ONE_PAGE) + 1 || 1;
|
|
359
|
+
expandedCalendarPageInfo.min = Math.floor(getDateTimeInfoByType(expandedValue, 'min') / CELL_CNT_IN_ONE_PAGE) + 1 || 1;
|
|
360
|
+
expandedCalendarPageInfo.sec = Math.floor(getDateTimeInfoByType(expandedValue, 'sec') / CELL_CNT_IN_ONE_PAGE) + 1 || 1;
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
expandedCalendarPageInfo = reactive({
|
|
364
|
+
year: new Date().getFullYear(),
|
|
365
|
+
month: new Date().getMonth() + 1,
|
|
366
|
+
hour: 1,
|
|
367
|
+
min: 1,
|
|
368
|
+
sec: 1,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// 현재 달력이 표현되는 월
|
|
373
|
+
const mainCalendarMonth = computed(() =>
|
|
374
|
+
MONTH_NAME_LIST[props.monthNotation][mainCalendarPageInfo.month - 1]);
|
|
375
|
+
// 다음페이지 달력이 표현되는 월
|
|
376
|
+
const expandedCalendarMonth = computed(() =>
|
|
377
|
+
MONTH_NAME_LIST[props.monthNotation][expandedCalendarPageInfo.month - 1]);
|
|
378
|
+
// 현재 달력에 표현되는 타입별 요일
|
|
379
|
+
const dayOfTheWeekList = computed(() =>
|
|
380
|
+
DAY_OF_THE_WEEK_NAME_LIST[props.dayOfTheWeekNotation]);
|
|
381
|
+
// mode: dateRange에 두 달력이 연속적인 경우
|
|
382
|
+
const isContinuousMonths = computed(
|
|
383
|
+
() => ['dateRange', 'dateTimeRange'].includes(props.mode)
|
|
384
|
+
&& (mainCalendarPageInfo.year === expandedCalendarPageInfo.year
|
|
385
|
+
&& mainCalendarPageInfo.month === expandedCalendarPageInfo.month),
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
onBeforeMount(() => {
|
|
389
|
+
validateModelValue();
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
selectedValue,
|
|
394
|
+
mainCalendarPageInfo,
|
|
395
|
+
expandedCalendarPageInfo,
|
|
396
|
+
mainCalendarMonth,
|
|
397
|
+
expandedCalendarMonth,
|
|
398
|
+
dayOfTheWeekList,
|
|
399
|
+
isContinuousMonths,
|
|
400
|
+
};
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
export const useCalendarDate = (param) => {
|
|
404
|
+
const { props, emit } = getCurrentInstance();
|
|
405
|
+
const { selectedValue, mainCalendarPageInfo, expandedCalendarPageInfo } = param;
|
|
406
|
+
|
|
407
|
+
// 메인 달력 테이블의 날짜 정보 (6X7, 2차원배열)
|
|
408
|
+
const mainCalendarTableInfo = reactive(getMatrixArr(CALENDAR_ROWS, CALENDAR_COLS));
|
|
409
|
+
// dateRange 모드의 확장된 달력 테이블의 날짜 정보
|
|
410
|
+
const expandedCalendarTableInfo = reactive(getMatrixArr(CALENDAR_ROWS, CALENDAR_COLS));
|
|
411
|
+
// 시간박스 정보
|
|
412
|
+
const mainTimeTableInfo = reactive({
|
|
413
|
+
hour: [],
|
|
414
|
+
min: [],
|
|
415
|
+
sec: [],
|
|
416
|
+
});
|
|
417
|
+
// dateTimeRange 모드의 확장된 달력 테이블의 시간 박스 정보
|
|
418
|
+
const expandedTimeTableInfo = reactive({
|
|
419
|
+
hour: [],
|
|
420
|
+
min: [],
|
|
421
|
+
sec: [],
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* calendar setting 하기 전 선택된 날짜가 disabledDate에 포함되는지 체크
|
|
426
|
+
* @param isRangeMode
|
|
427
|
+
* @param calendarType
|
|
428
|
+
* @param disabledDate
|
|
429
|
+
*/
|
|
430
|
+
const checkDisabledDate = ({ isRangeMode, calendarType, disabledDate }) => {
|
|
431
|
+
if (isRangeMode) {
|
|
432
|
+
if (calendarType === 'main' && selectedValue.value[0]) {
|
|
433
|
+
if (disabledDate && disabledDate(new Date(selectedValue.value[0]))) {
|
|
434
|
+
selectedValue.value[0] = '';
|
|
435
|
+
emit('update:modelValue', [...selectedValue.value]);
|
|
436
|
+
}
|
|
437
|
+
} else if (calendarType === 'expanded' && selectedValue.value[1]) {
|
|
438
|
+
if (disabledDate && disabledDate(new Date(selectedValue.value[1]))) {
|
|
439
|
+
selectedValue.value[1] = '';
|
|
440
|
+
emit('update:modelValue', [...selectedValue.value]);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
} else if (props.mode === 'dateMulti') {
|
|
444
|
+
let isUpdate = false;
|
|
445
|
+
selectedValue.value.forEach((value, index) => {
|
|
446
|
+
if (disabledDate && disabledDate(new Date(value))) {
|
|
447
|
+
selectedValue.value.splice(index, 1);
|
|
448
|
+
isUpdate = true;
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
if (isUpdate) {
|
|
452
|
+
emit('update:modelValue', [...selectedValue.value]);
|
|
453
|
+
}
|
|
454
|
+
} else if (disabledDate && disabledDate(new Date(selectedValue.value))) {
|
|
455
|
+
selectedValue.value = '';
|
|
456
|
+
emit('update:modelValue', selectedValue.value);
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Dropdown Calendar 날짜 정보 세팅하기
|
|
462
|
+
* @param calendarType - 달력 종류 ('main'|'expanded')
|
|
463
|
+
*/
|
|
464
|
+
const setCalendarDate = (calendarType) => {
|
|
465
|
+
const calendarPageInfo = calendarType === 'expanded'
|
|
466
|
+
? expandedCalendarPageInfo : mainCalendarPageInfo;
|
|
467
|
+
const calendarTableInfo = calendarType === 'expanded'
|
|
468
|
+
? expandedCalendarTableInfo : mainCalendarTableInfo;
|
|
469
|
+
|
|
470
|
+
let disabledDate = props.options.disabledDate;
|
|
471
|
+
if (disabledDate && Array.isArray(disabledDate)) {
|
|
472
|
+
disabledDate = calendarType === 'main' ? disabledDate[0] : disabledDate[1];
|
|
473
|
+
}
|
|
474
|
+
const isRangeMode = ['dateRange', 'dateTimeRange'].includes(props.mode);
|
|
475
|
+
|
|
476
|
+
checkDisabledDate({
|
|
477
|
+
isRangeMode,
|
|
478
|
+
calendarType,
|
|
479
|
+
disabledDate,
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
const TODAY_YMD = formatDateTime({
|
|
483
|
+
year: new Date().getFullYear(),
|
|
484
|
+
month: new Date().getMonth() + 1,
|
|
485
|
+
date: new Date().getDate(),
|
|
486
|
+
});
|
|
487
|
+
const PREV_MONTH = computed(() =>
|
|
488
|
+
((MONTH_CNT + calendarPageInfo.month - 1) % MONTH_CNT) || MONTH_CNT);
|
|
489
|
+
const NEXT_MONTH = computed(() =>
|
|
490
|
+
((calendarPageInfo.month + 1) % MONTH_CNT) || MONTH_CNT);
|
|
491
|
+
const YEAR_OF_PREV_MONTH = computed(() => (calendarPageInfo.month === 1
|
|
492
|
+
? calendarPageInfo.year - 1 : calendarPageInfo.year));
|
|
493
|
+
const YEAR_OF_NEXT_MONTH = computed(() => (calendarPageInfo.month === 12
|
|
494
|
+
? calendarPageInfo.year + 1 : calendarPageInfo.year));
|
|
495
|
+
// 이번달 1일의 요일
|
|
496
|
+
const dayOfWeekOnThe1stOfThisMonth = computed(() => getDayOfWeekOnThe1stOfMonth(
|
|
497
|
+
calendarPageInfo.year,
|
|
498
|
+
calendarPageInfo.month,
|
|
499
|
+
));
|
|
500
|
+
// 저번달 마지막 날짜
|
|
501
|
+
const lastDateOfPrevMonth = computed(() => getLastDateOfMonth(
|
|
502
|
+
calendarPageInfo.month === 1
|
|
503
|
+
? calendarPageInfo.year - 1 : calendarPageInfo.year,
|
|
504
|
+
(MONTH_CNT + calendarPageInfo.month - 1) % MONTH_CNT || MONTH_CNT,
|
|
505
|
+
));
|
|
506
|
+
// 이번달 마지막 날짜
|
|
507
|
+
const lastDateOfThisMonth = computed(() => getLastDateOfMonth(
|
|
508
|
+
calendarPageInfo.year,
|
|
509
|
+
calendarPageInfo.month,
|
|
510
|
+
));
|
|
511
|
+
|
|
512
|
+
let modelValue = '';
|
|
513
|
+
if (props.mode.includes('Time')) {
|
|
514
|
+
if (props.mode === 'dateTime') {
|
|
515
|
+
modelValue = selectedValue.value;
|
|
516
|
+
} else {
|
|
517
|
+
modelValue = calendarType === 'main' ? selectedValue.value[0] : selectedValue.value[1];
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
let monthDate = 0;
|
|
522
|
+
let year = 0;
|
|
523
|
+
let month = 0;
|
|
524
|
+
let date = 0;
|
|
525
|
+
let currDate = '';
|
|
526
|
+
// date 숫자 및 속성 세팅
|
|
527
|
+
const setDateInfo = (monthType, i, j) => {
|
|
528
|
+
currDate = formatDateTime({ year, month, date });
|
|
529
|
+
const isInvalidDate = isRangeMode
|
|
530
|
+
&& compareFromAndToDateTime(props.mode, calendarType, currDate, selectedValue.value);
|
|
531
|
+
|
|
532
|
+
// time 모드인 경우 현재 값의 시간을 가지고 테스트
|
|
533
|
+
const timeValue = modelValue?.split(' ')[1] ?? '';
|
|
534
|
+
|
|
535
|
+
const isDisabled = disabledDate
|
|
536
|
+
? disabledDate(new Date(`${currDate} ${timeValue}`)) : isInvalidDate;
|
|
537
|
+
|
|
538
|
+
const index = +(calendarType !== 'main');
|
|
539
|
+
const isRangeSelected = isRangeMode && selectedValue.value.length > index
|
|
540
|
+
&& selectedValue.value[index].split(' ')[0].includes(currDate);
|
|
541
|
+
const isSelected = !isDisabled && (isRangeMode
|
|
542
|
+
? monthType === '' && isRangeSelected
|
|
543
|
+
: selectedValue.value?.includes(currDate));
|
|
544
|
+
|
|
545
|
+
// mode가 dateRange일 때는 이전, 다음달에 selected 를 하지 않는다.
|
|
546
|
+
calendarTableInfo[i][j] = {
|
|
547
|
+
monthType: `${monthType}${isDisabled ? ' disabled' : ''}`,
|
|
548
|
+
isToday: TODAY_YMD === currDate,
|
|
549
|
+
isSelected,
|
|
550
|
+
year,
|
|
551
|
+
month,
|
|
552
|
+
date,
|
|
553
|
+
};
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
for (let i = 0; i < CALENDAR_ROWS; i++) {
|
|
557
|
+
for (let j = 0; j < CALENDAR_COLS; j++) {
|
|
558
|
+
if (i === 0) {
|
|
559
|
+
// 첫번째 주
|
|
560
|
+
if (dayOfWeekOnThe1stOfThisMonth.value !== 0) {
|
|
561
|
+
if (j < dayOfWeekOnThe1stOfThisMonth.value) {
|
|
562
|
+
year = YEAR_OF_PREV_MONTH.value;
|
|
563
|
+
month = PREV_MONTH.value;
|
|
564
|
+
date = lastDateOfPrevMonth.value - dayOfWeekOnThe1stOfThisMonth.value + 1 + j;
|
|
565
|
+
setDateInfo('prev', i, j);
|
|
566
|
+
} else {
|
|
567
|
+
monthDate++;
|
|
568
|
+
year = calendarPageInfo.year;
|
|
569
|
+
month = calendarPageInfo.month;
|
|
570
|
+
date = monthDate;
|
|
571
|
+
setDateInfo('', i, j);
|
|
572
|
+
}
|
|
573
|
+
} else {
|
|
574
|
+
year = YEAR_OF_PREV_MONTH.value;
|
|
575
|
+
month = PREV_MONTH.value;
|
|
576
|
+
date = lastDateOfPrevMonth.value - 6 + j;
|
|
577
|
+
setDateInfo('prev', i, j);
|
|
578
|
+
}
|
|
579
|
+
} else if (lastDateOfThisMonth.value <= monthDate) {
|
|
580
|
+
// 마지막 -1, 마지막 주의 다음달 날짜
|
|
581
|
+
monthDate++;
|
|
582
|
+
year = YEAR_OF_NEXT_MONTH.value;
|
|
583
|
+
month = NEXT_MONTH.value;
|
|
584
|
+
date = monthDate - lastDateOfThisMonth.value;
|
|
585
|
+
setDateInfo('next', i, j);
|
|
586
|
+
} else {
|
|
587
|
+
// 첫번째 주를 제외한 이번달 날짜
|
|
588
|
+
monthDate++;
|
|
589
|
+
year = calendarPageInfo.year;
|
|
590
|
+
month = calendarPageInfo.month;
|
|
591
|
+
date = monthDate;
|
|
592
|
+
setDateInfo('', i, j);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Calendar 시간 정보 세팅하기
|
|
600
|
+
*/
|
|
601
|
+
const setHmsTime = () => {
|
|
602
|
+
const timeFormat = props.options?.timeFormat;
|
|
603
|
+
const disabledDate = props.options?.disabledDate;
|
|
604
|
+
const mainTimeFormat = Array.isArray(timeFormat) ? timeFormat[0] : timeFormat;
|
|
605
|
+
const expandedTimeFormat = Array.isArray(timeFormat) ? timeFormat[1] : '';
|
|
606
|
+
const mainDateTimeValue = props.mode === 'dateTimeRange' ? selectedValue.value[0] : selectedValue.value;
|
|
607
|
+
const expandedDateTimeValue = props.mode === 'dateTimeRange' ? selectedValue.value[1] : '';
|
|
608
|
+
const mainDisabledDate = Array.isArray(disabledDate) ? disabledDate[0] : disabledDate;
|
|
609
|
+
const expandedDisabledDate = Array.isArray(disabledDate) ? disabledDate[1] : disabledDate;
|
|
610
|
+
|
|
611
|
+
const compareDateTimeValue = (calendarType, timeType, value) => {
|
|
612
|
+
const dateTimeValue = calendarType === 'main' ? mainDateTimeValue : expandedDateTimeValue;
|
|
613
|
+
const disabledDateFunc = calendarType === 'main' ? mainDisabledDate : expandedDisabledDate;
|
|
614
|
+
if (!dateTimeValue) {
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const date = dateTimeValue.split(' ')[0];
|
|
619
|
+
let hour = getDateTimeInfoByType(dateTimeValue, 'hour');
|
|
620
|
+
let min = getDateTimeInfoByType(dateTimeValue, 'min');
|
|
621
|
+
let sec = getDateTimeInfoByType(dateTimeValue, 'sec');
|
|
622
|
+
|
|
623
|
+
if (timeType === 'hour') {
|
|
624
|
+
hour = value;
|
|
625
|
+
} else if (timeType === 'min') {
|
|
626
|
+
min = value;
|
|
627
|
+
} else if (timeType === 'sec') {
|
|
628
|
+
sec = value;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const targetDateTimeValue = `${date} ${lpadToTwoDigits(hour)}:${lpadToTwoDigits(min)}:${lpadToTwoDigits(sec)}`;
|
|
632
|
+
if (disabledDateFunc && disabledDateFunc(new Date(targetDateTimeValue))) {
|
|
633
|
+
return true;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
return !disabledDateFunc && compareFromAndToDateTime(
|
|
637
|
+
props.mode,
|
|
638
|
+
calendarType,
|
|
639
|
+
targetDateTimeValue,
|
|
640
|
+
selectedValue.value,
|
|
641
|
+
);
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
['hour', 'min', 'sec'].forEach((v) => {
|
|
645
|
+
let cnt = SEC_CNT;
|
|
646
|
+
if (v === 'hour') {
|
|
647
|
+
cnt = HOUR_CNT;
|
|
648
|
+
} else if (v === 'min') {
|
|
649
|
+
cnt = MIN_CNT;
|
|
650
|
+
}
|
|
651
|
+
const mainTimeValue = mainDateTimeValue && mainDateTimeValue.length > 0
|
|
652
|
+
? getTimeInfoByTimeFormat(mainTimeFormat, mainDateTimeValue, v) : -1;
|
|
653
|
+
const expandedTimeValue = expandedDateTimeValue && expandedDateTimeValue.length > 0
|
|
654
|
+
? getTimeInfoByTimeFormat(expandedTimeFormat, expandedDateTimeValue, v) : -1;
|
|
655
|
+
for (let i = 0; i < cnt; i++) {
|
|
656
|
+
let isDisabled = props.mode === 'dateTimeRange' && compareDateTimeValue('main', v, i);
|
|
657
|
+
mainTimeTableInfo[v][i] = {
|
|
658
|
+
timeType: v,
|
|
659
|
+
num: i,
|
|
660
|
+
isSelected: !isDisabled && mainTimeValue === i,
|
|
661
|
+
isDisabled,
|
|
662
|
+
};
|
|
663
|
+
if (props.mode === 'dateTimeRange') {
|
|
664
|
+
isDisabled = compareDateTimeValue('expanded', v, i);
|
|
665
|
+
expandedTimeTableInfo[v][i] = {
|
|
666
|
+
timeType: v,
|
|
667
|
+
num: i,
|
|
668
|
+
isSelected: !isDisabled && expandedTimeValue === i,
|
|
669
|
+
isDisabled,
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* HMS 영역 내 tr, td, 페이지에 맞는 시간 정보 가져오기
|
|
678
|
+
* @param timeType - {'hour'|'min'|'sec'}
|
|
679
|
+
* @param i - rows
|
|
680
|
+
* @param j - cols
|
|
681
|
+
* @param calendarType - {'main'|'expanded'}
|
|
682
|
+
* @returns {object} - cellInfo
|
|
683
|
+
*/
|
|
684
|
+
const getTimeInfo = (timeType, i, j, calendarType) => {
|
|
685
|
+
const pageInfo = calendarType === 'main' ? mainCalendarPageInfo : expandedCalendarPageInfo;
|
|
686
|
+
const timeInfo = calendarType === 'main' ? mainTimeTableInfo : expandedTimeTableInfo;
|
|
687
|
+
const currPage = pageInfo[timeType] - 1;
|
|
688
|
+
const currRowIdx = i - 1;
|
|
689
|
+
const currColIdx = j - 1;
|
|
690
|
+
const currIdx = (currPage * CELL_CNT_IN_ONE_PAGE)
|
|
691
|
+
+ (currRowIdx * CELL_CNT_IN_ONE_ROW) + currColIdx;
|
|
692
|
+
return timeInfo[timeType][currIdx];
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
return {
|
|
696
|
+
mainCalendarTableInfo,
|
|
697
|
+
expandedCalendarTableInfo,
|
|
698
|
+
mainTimeTableInfo,
|
|
699
|
+
expandedTimeTableInfo,
|
|
700
|
+
setCalendarDate,
|
|
701
|
+
setHmsTime,
|
|
702
|
+
getTimeInfo,
|
|
703
|
+
};
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
export const useEvent = (param) => {
|
|
707
|
+
const { props, emit } = getCurrentInstance();
|
|
708
|
+
const timeFormat = props.options?.timeFormat;
|
|
709
|
+
const {
|
|
710
|
+
selectedValue,
|
|
711
|
+
mainCalendarPageInfo,
|
|
712
|
+
expandedCalendarPageInfo,
|
|
713
|
+
mainTimeTableInfo,
|
|
714
|
+
expandedTimeTableInfo,
|
|
715
|
+
setCalendarDate,
|
|
716
|
+
setHmsTime,
|
|
717
|
+
} = param;
|
|
718
|
+
|
|
719
|
+
// dateRange mode에서 클릭하여 첫번째 선택된 날짜
|
|
720
|
+
const dateRangeClickedDate = ref('');
|
|
721
|
+
// dateRange mode에서 클릭한번 후 커서에 따라 날짜를 마우스오버하는 경우 dynamic argument로 이벤트명 설정
|
|
722
|
+
const calendarEventName = ref(null);
|
|
723
|
+
// dateTime 또는 dateTimeRange에서 timeFormat이 있는 경우 event 막음
|
|
724
|
+
const mainTimeFormat = Array.isArray(timeFormat) ? timeFormat[0] : timeFormat;
|
|
725
|
+
const expandedTimeFormat = Array.isArray(timeFormat) ? timeFormat[1] : '';
|
|
726
|
+
const preventTimeEventType = {
|
|
727
|
+
main: {
|
|
728
|
+
hour: mainTimeFormat && mainTimeFormat.split(':')[0] !== 'HH',
|
|
729
|
+
min: mainTimeFormat && mainTimeFormat.split(':')[1] !== 'mm',
|
|
730
|
+
sec: mainTimeFormat && mainTimeFormat.split(':')[2] !== 'ss',
|
|
731
|
+
},
|
|
732
|
+
expanded: {
|
|
733
|
+
hour: expandedTimeFormat && expandedTimeFormat.split(':')[0] !== 'HH',
|
|
734
|
+
min: expandedTimeFormat && expandedTimeFormat.split(':')[1] !== 'mm',
|
|
735
|
+
sec: expandedTimeFormat && expandedTimeFormat.split(':')[2] !== 'ss',
|
|
736
|
+
},
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* 입력받은 dateTime object에 calendar date, time 영역 페이지 세팅
|
|
741
|
+
* @param calendarType - 달력 종류 ('main'|'expanded')
|
|
742
|
+
* @param dateTime - 입력된 페이지 정보 ({ year, month, hour, min, sec })
|
|
743
|
+
*/
|
|
744
|
+
const setCalendarPageInfo = (calendarType, dateTime) => {
|
|
745
|
+
const calendarPageInfo = calendarType === 'expanded'
|
|
746
|
+
? expandedCalendarPageInfo : mainCalendarPageInfo;
|
|
747
|
+
const { year, month, hour, min, sec } = dateTime;
|
|
748
|
+
if (year) {
|
|
749
|
+
calendarPageInfo.year = year;
|
|
750
|
+
}
|
|
751
|
+
if (month) {
|
|
752
|
+
calendarPageInfo.month = month;
|
|
753
|
+
}
|
|
754
|
+
if (hour) {
|
|
755
|
+
calendarPageInfo.hour = hour;
|
|
756
|
+
}
|
|
757
|
+
if (min) {
|
|
758
|
+
calendarPageInfo.min = min;
|
|
759
|
+
}
|
|
760
|
+
if (sec) {
|
|
761
|
+
calendarPageInfo.sec = sec;
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* value를 Array로 담아 페이지 세팅
|
|
767
|
+
* @param valueList
|
|
768
|
+
*/
|
|
769
|
+
const updateCalendarPage = (valueList) => {
|
|
770
|
+
valueList?.forEach((currValue, index) => {
|
|
771
|
+
const changeCalendarType = index === 0 ? 'main' : 'expanded';
|
|
772
|
+
setCalendarPageInfo(changeCalendarType, {
|
|
773
|
+
year: getDateTimeInfoByType(currValue, 'year'),
|
|
774
|
+
month: getDateTimeInfoByType(currValue, 'month'),
|
|
775
|
+
hour: Math.floor(getDateTimeInfoByType(currValue, 'hour') / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
776
|
+
min: Math.floor(getDateTimeInfoByType(currValue, 'min') / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
777
|
+
sec: Math.floor(getDateTimeInfoByType(currValue, 'sec') / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
778
|
+
});
|
|
779
|
+
setCalendarDate(changeCalendarType);
|
|
780
|
+
});
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Calendar 의 Month 이동시키기 (이전, 이후)
|
|
785
|
+
* expandedCalendar가 존재하는 경우(mode: timeRange)
|
|
786
|
+
* mainCalendar의 date는 expandedCalendar의 날짜를 넘길 수 없다
|
|
787
|
+
* mainCalendar year, month < expandedCalendar year, month
|
|
788
|
+
* @param calendarType - 달력 종류 ('main'|'expanded')
|
|
789
|
+
* @param type - {'prev'|'next'}
|
|
790
|
+
*/
|
|
791
|
+
const moveMonth = (calendarType, type) => {
|
|
792
|
+
const isDateRangeMode = ['dateRange', 'dateTimeRange'].includes(props.mode);
|
|
793
|
+
let calendarPageInfo = mainCalendarPageInfo;
|
|
794
|
+
if (!isDateRangeMode) {
|
|
795
|
+
if (type === 'prev') {
|
|
796
|
+
if (calendarPageInfo.month === 1) {
|
|
797
|
+
calendarPageInfo.year -= 1;
|
|
798
|
+
calendarPageInfo.month = 12;
|
|
799
|
+
} else {
|
|
800
|
+
calendarPageInfo.month -= 1;
|
|
801
|
+
}
|
|
802
|
+
} else if (calendarPageInfo.month === 12) {
|
|
803
|
+
calendarPageInfo.year += 1;
|
|
804
|
+
calendarPageInfo.month = 1;
|
|
805
|
+
} else {
|
|
806
|
+
calendarPageInfo.month += 1;
|
|
807
|
+
}
|
|
808
|
+
} else {
|
|
809
|
+
calendarPageInfo = calendarType === 'expanded'
|
|
810
|
+
? expandedCalendarPageInfo : mainCalendarPageInfo;
|
|
811
|
+
|
|
812
|
+
// 두 달력간의 연속 여부 (메인 달력 + 1Month === 확장된 달력)
|
|
813
|
+
// mainCalendar Month < expandedCalendar Month
|
|
814
|
+
const isContinuousMonths = expandedCalendarPageInfo.year === mainCalendarPageInfo.year
|
|
815
|
+
&& expandedCalendarPageInfo.month === mainCalendarPageInfo.month;
|
|
816
|
+
if (type === 'prev') {
|
|
817
|
+
if (isContinuousMonths && calendarType === 'expanded') {
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (calendarPageInfo.month === 1) {
|
|
821
|
+
calendarPageInfo.year -= 1;
|
|
822
|
+
calendarPageInfo.month = 12;
|
|
823
|
+
} else {
|
|
824
|
+
calendarPageInfo.month -= 1;
|
|
825
|
+
}
|
|
826
|
+
} else {
|
|
827
|
+
if (isContinuousMonths && calendarType === 'main') {
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
if (calendarPageInfo.month === 12) {
|
|
831
|
+
calendarPageInfo.year += 1;
|
|
832
|
+
calendarPageInfo.month = 1;
|
|
833
|
+
} else {
|
|
834
|
+
calendarPageInfo.month += 1;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Calendar Header의 prev, next 아이콘 클릭 이벤트
|
|
842
|
+
* @param calendarType - 달력 종류 ('main'|'expanded')
|
|
843
|
+
* @param type - 이전달, 다음달 ('prev'|'next')
|
|
844
|
+
*/
|
|
845
|
+
const clickPrevNextBtn = (calendarType, type) => {
|
|
846
|
+
moveMonth(calendarType, type);
|
|
847
|
+
setCalendarDate(calendarType);
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Calendar Date 일자 클릭 이벤트
|
|
852
|
+
* @param calendarType - {main|expanded}
|
|
853
|
+
* @param dateInfo
|
|
854
|
+
*/
|
|
855
|
+
const clickDate = (calendarType, dateInfo) => {
|
|
856
|
+
const { year, month, date, monthType } = dateInfo;
|
|
857
|
+
const CURR_DATE_STR = formatDateTime({ year, month, date });
|
|
858
|
+
const isExistCurrDate = props.modelValue ? (Array.isArray(props.modelValue)
|
|
859
|
+
? props.modelValue?.map(v => v.split(' ')[0])
|
|
860
|
+
: props.modelValue.split(' ')[0])
|
|
861
|
+
.includes(CURR_DATE_STR) : false;
|
|
862
|
+
|
|
863
|
+
let disabledDate = props.options.disabledDate;
|
|
864
|
+
if (disabledDate && Array.isArray(disabledDate)) {
|
|
865
|
+
disabledDate = calendarType === 'main' ? disabledDate[0] : disabledDate[1];
|
|
866
|
+
}
|
|
867
|
+
// 제한된 날짜는 선택할 수 없다.
|
|
868
|
+
if (disabledDate && disabledDate(new Date(CURR_DATE_STR)) && !isExistCurrDate) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
const calendarPageInfo = calendarType === 'main' ? mainCalendarPageInfo : expandedCalendarPageInfo;
|
|
873
|
+
const PREV_MONTH = ((MONTH_CNT + calendarPageInfo.month - 1) % MONTH_CNT) || MONTH_CNT;
|
|
874
|
+
const YEAR_OF_PREV_MONTH = calendarPageInfo.month === 1
|
|
875
|
+
? calendarPageInfo.year - 1 : calendarPageInfo.year;
|
|
876
|
+
const NEXT_MONTH = ((calendarPageInfo.month + 1) % MONTH_CNT) || MONTH_CNT;
|
|
877
|
+
const YEAR_OF_NEXT_MONTH = calendarPageInfo.month === 12
|
|
878
|
+
? calendarPageInfo.year + 1 : calendarPageInfo.year;
|
|
879
|
+
|
|
880
|
+
const moveDispCalendarMonth = () => {
|
|
881
|
+
if (monthType.includes('prev')) {
|
|
882
|
+
calendarPageInfo.year = YEAR_OF_PREV_MONTH;
|
|
883
|
+
calendarPageInfo.month = PREV_MONTH;
|
|
884
|
+
} else if (monthType.includes('next')) {
|
|
885
|
+
calendarPageInfo.year = YEAR_OF_NEXT_MONTH;
|
|
886
|
+
calendarPageInfo.month = NEXT_MONTH;
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
const setRangeModeDateByIndex = (currIndex, currDate) => {
|
|
891
|
+
if (!disabledDate
|
|
892
|
+
&& compareFromAndToDateTime(props.mode, calendarType, currDate, selectedValue.value)) {
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
selectedValue.value[currIndex] = currDate;
|
|
897
|
+
moveDispCalendarMonth();
|
|
898
|
+
updateCalendarPage(selectedValue.value);
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
switch (props.mode) {
|
|
902
|
+
case 'date':
|
|
903
|
+
selectedValue.value = CURR_DATE_STR;
|
|
904
|
+
moveDispCalendarMonth();
|
|
905
|
+
emit('update:modelValue', CURR_DATE_STR);
|
|
906
|
+
setCalendarDate('main');
|
|
907
|
+
break;
|
|
908
|
+
case 'dateTime': {
|
|
909
|
+
const isExistTime = !!(selectedValue.value?.split(' ')[1]);
|
|
910
|
+
const CURR_TIME_HMS = isExistTime
|
|
911
|
+
? selectedValue.value?.split(' ')[1] : '00:00:00';
|
|
912
|
+
selectedValue.value = getChangedValueByTimeFormat(
|
|
913
|
+
timeFormat,
|
|
914
|
+
`${CURR_DATE_STR} ${CURR_TIME_HMS}`,
|
|
915
|
+
);
|
|
916
|
+
moveDispCalendarMonth();
|
|
917
|
+
emit('update:modelValue', selectedValue.value);
|
|
918
|
+
setCalendarDate('main');
|
|
919
|
+
if (!isExistTime) {
|
|
920
|
+
const currTime = selectedValue.value.split(' ')[1].split(':');
|
|
921
|
+
setCalendarPageInfo('main', {
|
|
922
|
+
hour: Math.floor(currTime[0] / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
923
|
+
min: Math.floor(currTime[1] / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
924
|
+
sec: Math.floor(currTime[2] / CELL_CNT_IN_ONE_PAGE) + 1,
|
|
925
|
+
});
|
|
926
|
+
setHmsTime();
|
|
927
|
+
}
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
930
|
+
case 'dateMulti': {
|
|
931
|
+
const multiType = props.options.multiType;
|
|
932
|
+
const multiDayLimit = props.options.multiDayLimit;
|
|
933
|
+
if (multiType === 'date') {
|
|
934
|
+
const selectedIdx = selectedValue.value.indexOf(CURR_DATE_STR);
|
|
935
|
+
if (selectedIdx > -1) {
|
|
936
|
+
selectedValue.value.splice(selectedIdx, 1);
|
|
937
|
+
emit('update:modelValue', [...selectedValue.value]);
|
|
938
|
+
} else if (selectedValue.value.length < multiDayLimit) {
|
|
939
|
+
selectedValue.value.push(CURR_DATE_STR);
|
|
940
|
+
moveDispCalendarMonth();
|
|
941
|
+
emit('update:modelValue', [...selectedValue.value]);
|
|
942
|
+
}
|
|
943
|
+
} else if (multiType === 'week' || multiType === 'weekday') {
|
|
944
|
+
const NUMBER_OF_DAYS_IN_RANGE = multiType === 'week' ? 7 : 5; // 범위 내 선택된 날짜 개수
|
|
945
|
+
const DIFF_UNTIL_THE_LAST_DATE = multiType === 'week' ? 6 : 5; // 한 주의 마지막 날짜까지의 차이
|
|
946
|
+
const exactSelectedDate = new Date(`${CURR_DATE_STR} 00:00:00`);
|
|
947
|
+
const dayOfTheWeekOfTheSelectedDate = exactSelectedDate.getDay();
|
|
948
|
+
const diffFromTheLastDay = DIFF_UNTIL_THE_LAST_DATE - dayOfTheWeekOfTheSelectedDate;
|
|
949
|
+
const theLastDayTime = exactSelectedDate.getTime() + (ONE_DAY_MS * diffFromTheLastDay);
|
|
950
|
+
|
|
951
|
+
for (let i = 0; i < NUMBER_OF_DAYS_IN_RANGE; i++) {
|
|
952
|
+
const loopYear = new Date(theLastDayTime - (i * ONE_DAY_MS)).getFullYear();
|
|
953
|
+
const loopMonth = new Date(theLastDayTime - (i * ONE_DAY_MS)).getMonth() + 1;
|
|
954
|
+
const loopDate = new Date(theLastDayTime - (i * ONE_DAY_MS)).getDate();
|
|
955
|
+
const dateStr = `${loopYear}-${lpadToTwoDigits(loopMonth)}-${lpadToTwoDigits(loopDate)}`;
|
|
956
|
+
if (i === 0) {
|
|
957
|
+
if (selectedValue.value.includes(dateStr)) {
|
|
958
|
+
selectedValue.value.splice(0);
|
|
959
|
+
break;
|
|
960
|
+
} else {
|
|
961
|
+
selectedValue.value.splice(0);
|
|
962
|
+
moveDispCalendarMonth();
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
if (!disabledDate || !disabledDate(new Date(dateStr))) {
|
|
966
|
+
selectedValue.value.unshift(dateStr);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
emit('update:modelValue', [...selectedValue.value]);
|
|
970
|
+
}
|
|
971
|
+
setCalendarDate('main');
|
|
972
|
+
break;
|
|
973
|
+
}
|
|
974
|
+
case 'dateRange': {
|
|
975
|
+
if (!selectedValue.value.length) {
|
|
976
|
+
selectedValue.value.push(CURR_DATE_STR);
|
|
977
|
+
selectedValue.value.push(CURR_DATE_STR);
|
|
978
|
+
updateCalendarPage(selectedValue.value);
|
|
979
|
+
} else {
|
|
980
|
+
setRangeModeDateByIndex(calendarType !== 'main' | 0, CURR_DATE_STR);
|
|
981
|
+
}
|
|
982
|
+
emit('update:modelValue', [...selectedValue.value]);
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
case 'dateTimeRange': {
|
|
986
|
+
if (!selectedValue.value.length) {
|
|
987
|
+
let fromDate = `${CURR_DATE_STR} 00:00:00`;
|
|
988
|
+
let toDate = `${CURR_DATE_STR} 00:00:00`;
|
|
989
|
+
if (timeFormat && timeFormat.length) {
|
|
990
|
+
fromDate = getChangedValueByTimeFormat(
|
|
991
|
+
timeFormat[0],
|
|
992
|
+
fromDate,
|
|
993
|
+
);
|
|
994
|
+
toDate = getChangedValueByTimeFormat(
|
|
995
|
+
timeFormat[1],
|
|
996
|
+
toDate,
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
selectedValue.value.push(fromDate);
|
|
1000
|
+
selectedValue.value.push(toDate);
|
|
1001
|
+
|
|
1002
|
+
updateCalendarPage(selectedValue.value);
|
|
1003
|
+
setHmsTime();
|
|
1004
|
+
} else {
|
|
1005
|
+
const currIndex = calendarType !== 'main' | 0;
|
|
1006
|
+
const CURR_TIME_HMS = selectedValue.value[currIndex]?.split(' ')[1] || '00:00:00';
|
|
1007
|
+
|
|
1008
|
+
let currDate = `${CURR_DATE_STR} ${CURR_TIME_HMS}`;
|
|
1009
|
+
if (timeFormat && timeFormat.length) {
|
|
1010
|
+
currDate = getChangedValueByTimeFormat(
|
|
1011
|
+
timeFormat[currIndex],
|
|
1012
|
+
currDate,
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
setRangeModeDateByIndex(currIndex, currDate);
|
|
1016
|
+
}
|
|
1017
|
+
emit('update:modelValue', [...selectedValue.value]);
|
|
1018
|
+
break;
|
|
1019
|
+
}
|
|
1020
|
+
default:
|
|
1021
|
+
break;
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Calendar mode: dateTime인 경우 HMS 이동 화살표 클릭 이벤트
|
|
1027
|
+
* @param calendarType - {main|expanded}
|
|
1028
|
+
* @param timeType - {hour|min|sec}
|
|
1029
|
+
* @param arrow - {up|down}
|
|
1030
|
+
*/
|
|
1031
|
+
const clickHmsBtn = (calendarType, timeType, arrow) => {
|
|
1032
|
+
if (preventTimeEventType[calendarType][timeType]) {
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
const calendarPageInfo = calendarType === 'expanded'
|
|
1037
|
+
? expandedCalendarPageInfo : mainCalendarPageInfo;
|
|
1038
|
+
const FIRST_PAGE = 1;
|
|
1039
|
+
const HOUR_MAX_PAGE = 2;
|
|
1040
|
+
const MINUTE_MAX_PAGE = 5;
|
|
1041
|
+
const SECOND_MAX_PAGE = 5;
|
|
1042
|
+
if (timeType === 'hour') {
|
|
1043
|
+
if (arrow === 'down' && calendarPageInfo.hour < HOUR_MAX_PAGE) {
|
|
1044
|
+
calendarPageInfo.hour++;
|
|
1045
|
+
} else if (arrow === 'up' && calendarPageInfo.hour > FIRST_PAGE) {
|
|
1046
|
+
calendarPageInfo.hour--;
|
|
1047
|
+
}
|
|
1048
|
+
} else if (timeType === 'min') {
|
|
1049
|
+
if (arrow === 'down' && calendarPageInfo.min < MINUTE_MAX_PAGE) {
|
|
1050
|
+
calendarPageInfo.min++;
|
|
1051
|
+
} else if (arrow === 'up' && calendarPageInfo.min > FIRST_PAGE) {
|
|
1052
|
+
calendarPageInfo.min--;
|
|
1053
|
+
}
|
|
1054
|
+
} else if (timeType === 'sec') {
|
|
1055
|
+
if (arrow === 'down' && calendarPageInfo.sec < SECOND_MAX_PAGE) {
|
|
1056
|
+
calendarPageInfo.sec++;
|
|
1057
|
+
} else if (arrow === 'up' && calendarPageInfo.sec > FIRST_PAGE) {
|
|
1058
|
+
calendarPageInfo.sec--;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* Click cell In HMS area
|
|
1065
|
+
* @param calendarType - {main|expanded}
|
|
1066
|
+
* @param timeType - {hour|min|sec}
|
|
1067
|
+
* @param i - row
|
|
1068
|
+
* @param j - col
|
|
1069
|
+
*/
|
|
1070
|
+
const clickTime = (calendarType, timeType, i, j) => {
|
|
1071
|
+
if (preventTimeEventType[calendarType][timeType]) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
const calendarPageInfo = calendarType === 'expanded'
|
|
1076
|
+
? expandedCalendarPageInfo : mainCalendarPageInfo;
|
|
1077
|
+
const timeInfo = calendarType === 'main'
|
|
1078
|
+
? mainTimeTableInfo : expandedTimeTableInfo;
|
|
1079
|
+
const currPage = calendarPageInfo[timeType] - 1;
|
|
1080
|
+
const currRowIdx = i - 1;
|
|
1081
|
+
const currColIdx = j - 1;
|
|
1082
|
+
const clickedNum = (currPage * CELL_CNT_IN_ONE_PAGE)
|
|
1083
|
+
+ (currRowIdx * CELL_CNT_IN_ONE_ROW) + currColIdx;
|
|
1084
|
+
|
|
1085
|
+
if (timeInfo[timeType][clickedNum]?.isDisabled) {
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
const TODAY = new Date();
|
|
1090
|
+
const TODAY_INFO = {
|
|
1091
|
+
year: TODAY.getFullYear(),
|
|
1092
|
+
month: TODAY.getMonth() + 1,
|
|
1093
|
+
date: TODAY.getDate(),
|
|
1094
|
+
};
|
|
1095
|
+
let EXIST_MODEL = true;
|
|
1096
|
+
let valueListByUpdatePage = [];
|
|
1097
|
+
|
|
1098
|
+
const getTimeValueByType = () => {
|
|
1099
|
+
let targetTimeValue;
|
|
1100
|
+
if (timeType === 'hour') {
|
|
1101
|
+
targetTimeValue = `${lpadToTwoDigits(clickedNum)}:00:00'`;
|
|
1102
|
+
} else if (timeType === 'min') {
|
|
1103
|
+
targetTimeValue = `00:${lpadToTwoDigits(clickedNum)}:00`;
|
|
1104
|
+
} else if (timeType === 'sec') {
|
|
1105
|
+
targetTimeValue = `00:00:${lpadToTwoDigits(clickedNum)}`;
|
|
1106
|
+
}
|
|
1107
|
+
return `${formatDateTime(TODAY_INFO)} ${targetTimeValue}`;
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
const getChangedValue = (targetValue) => {
|
|
1111
|
+
const HOUR_START_IDX = 11;
|
|
1112
|
+
const MIN_START_IDX = 14;
|
|
1113
|
+
const SEC_START_IDX = 17;
|
|
1114
|
+
const REPLACE_TEXT_SIZE = 2;
|
|
1115
|
+
let START_IDX = HOUR_START_IDX;
|
|
1116
|
+
if (timeType === 'min') {
|
|
1117
|
+
START_IDX = MIN_START_IDX;
|
|
1118
|
+
} else if (timeType === 'sec') {
|
|
1119
|
+
START_IDX = SEC_START_IDX;
|
|
1120
|
+
}
|
|
1121
|
+
return `${targetValue?.substr(0, START_IDX)}`
|
|
1122
|
+
+ `${lpadToTwoDigits(clickedNum)}${targetValue?.substr(START_IDX + REPLACE_TEXT_SIZE)}`;
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
if (props.mode === 'dateTime') {
|
|
1126
|
+
if (!props.modelValue) {
|
|
1127
|
+
EXIST_MODEL = false;
|
|
1128
|
+
selectedValue.value = getChangedValueByTimeFormat(
|
|
1129
|
+
timeFormat,
|
|
1130
|
+
getTimeValueByType(),
|
|
1131
|
+
);
|
|
1132
|
+
emit('update:modelValue', selectedValue.value);
|
|
1133
|
+
} else {
|
|
1134
|
+
selectedValue.value = getChangedValueByTimeFormat(
|
|
1135
|
+
timeFormat,
|
|
1136
|
+
getChangedValue(props.modelValue),
|
|
1137
|
+
);
|
|
1138
|
+
emit('update:modelValue', selectedValue.value);
|
|
1139
|
+
}
|
|
1140
|
+
valueListByUpdatePage.push(selectedValue.value);
|
|
1141
|
+
} else {
|
|
1142
|
+
const index = calendarType !== 'main' | 0;
|
|
1143
|
+
if (!props.modelValue.length) {
|
|
1144
|
+
const timeValue = getTimeValueByType();
|
|
1145
|
+
selectedValue.value = [timeValue, timeValue];
|
|
1146
|
+
|
|
1147
|
+
if (timeFormat && timeFormat.length) {
|
|
1148
|
+
selectedValue.value = [...selectedValue.value
|
|
1149
|
+
.map((v, idx) => getChangedValueByTimeFormat(timeFormat[idx], v))];
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
EXIST_MODEL = false;
|
|
1153
|
+
valueListByUpdatePage = selectedValue.value;
|
|
1154
|
+
} else {
|
|
1155
|
+
let currDateTime = getChangedValue(props.modelValue[index]);
|
|
1156
|
+
if (timeFormat && timeFormat.length) {
|
|
1157
|
+
currDateTime = getChangedValueByTimeFormat(
|
|
1158
|
+
timeFormat[index],
|
|
1159
|
+
currDateTime,
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
selectedValue.value[index] = currDateTime;
|
|
1164
|
+
}
|
|
1165
|
+
emit('update:modelValue', [...selectedValue.value]);
|
|
1166
|
+
}
|
|
1167
|
+
setHmsTime();
|
|
1168
|
+
// dateTime의 v-model값이 없는 경우 time area를 클릭하였을 때 date의 값은 today로 세팅
|
|
1169
|
+
if (!EXIST_MODEL) {
|
|
1170
|
+
updateCalendarPage(valueListByUpdatePage);
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
/**
|
|
1175
|
+
* Wheel up or wheel down In Calendar Month(tbody) area
|
|
1176
|
+
* @param calendarType - {main|expanded}
|
|
1177
|
+
* @param e
|
|
1178
|
+
*/
|
|
1179
|
+
const wheelMonth = (calendarType, e) => {
|
|
1180
|
+
moveMonth(calendarType, e.deltaY > 0 ? 'next' : 'prev');
|
|
1181
|
+
setCalendarDate(calendarType);
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* Wheel up or wheel down In Calendar Time(HMS) area
|
|
1186
|
+
* @param calendarType - {main|expanded}
|
|
1187
|
+
* @param timeType - {hour|min|sec}
|
|
1188
|
+
* @param e
|
|
1189
|
+
*/
|
|
1190
|
+
const wheelTime = (calendarType, timeType, e) => {
|
|
1191
|
+
if (preventTimeEventType[calendarType][timeType]) {
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
clickHmsBtn(calendarType, timeType, e.deltaY > 0 ? 'down' : 'up');
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
/**
|
|
1199
|
+
* dateRange 모드에서 한번 클릭 후 날짜에 마우스무브하는 경우
|
|
1200
|
+
* 커서 위에 있는 날짜까지의 selectedValue의 영역 선택한 selection 로직
|
|
1201
|
+
* 일반적인 마우스무브 로직에 성능향상을 위한 throttle 10ms를 설정
|
|
1202
|
+
* @param calendarType - 캘린더 종류 ('main'|'expanded')
|
|
1203
|
+
* @param e - 마우스이벤트
|
|
1204
|
+
* @type {function(): (*)}
|
|
1205
|
+
*/
|
|
1206
|
+
const onMousemoveDate = throttle((calendarType, e) => {
|
|
1207
|
+
const target = e.target.tagName === 'TD' ? e.target : e.target.parentElement;
|
|
1208
|
+
const isDisabled = target.classList.contains('disabled');
|
|
1209
|
+
const isPrev = target.classList.contains('prev');
|
|
1210
|
+
const isNext = target.classList.contains('next');
|
|
1211
|
+
if (target.classList.length > 0 && !isDisabled) {
|
|
1212
|
+
const calendarPageInfo = calendarType === 'main' ? mainCalendarPageInfo : expandedCalendarPageInfo;
|
|
1213
|
+
let yearMonth = {
|
|
1214
|
+
year: +calendarPageInfo.year,
|
|
1215
|
+
month: +calendarPageInfo.month,
|
|
1216
|
+
date: e.target.innerText,
|
|
1217
|
+
};
|
|
1218
|
+
// 달력 내 이전달, 다음달 일자의 경우 연, 월 보정
|
|
1219
|
+
if (isPrev) {
|
|
1220
|
+
yearMonth = { ...yearMonth, ...getSideMonthCalendarInfo('prev', yearMonth.year, yearMonth.month) };
|
|
1221
|
+
} else if (isNext) {
|
|
1222
|
+
yearMonth = { ...yearMonth, ...getSideMonthCalendarInfo('next', yearMonth.year, yearMonth.month) };
|
|
1223
|
+
}
|
|
1224
|
+
const STANDARD_DATE_STR = dateRangeClickedDate.value;
|
|
1225
|
+
const MOUSEMOVE_DATE_STR = formatDateTime({
|
|
1226
|
+
year: yearMonth.year,
|
|
1227
|
+
month: yearMonth.month,
|
|
1228
|
+
date: yearMonth.date,
|
|
1229
|
+
});
|
|
1230
|
+
|
|
1231
|
+
// fromDate ~ toDate selection 순서 세팅
|
|
1232
|
+
if (getDateMs(MOUSEMOVE_DATE_STR) < getDateMs(STANDARD_DATE_STR)) {
|
|
1233
|
+
selectedValue.value[0] = MOUSEMOVE_DATE_STR;
|
|
1234
|
+
selectedValue.value[1] = STANDARD_DATE_STR;
|
|
1235
|
+
} else {
|
|
1236
|
+
selectedValue.value[0] = STANDARD_DATE_STR;
|
|
1237
|
+
selectedValue.value[1] = MOUSEMOVE_DATE_STR;
|
|
1238
|
+
}
|
|
1239
|
+
setCalendarDate('main');
|
|
1240
|
+
setCalendarDate('expanded');
|
|
1241
|
+
}
|
|
1242
|
+
}, 10);
|
|
1243
|
+
|
|
1244
|
+
watch(
|
|
1245
|
+
() => props.modelValue,
|
|
1246
|
+
(curr) => {
|
|
1247
|
+
selectedValue.value = curr;
|
|
1248
|
+
|
|
1249
|
+
if (props.mode.includes('Time')) {
|
|
1250
|
+
let updateValue = [];
|
|
1251
|
+
if (props.mode === 'dateTime') {
|
|
1252
|
+
updateValue = [selectedValue.value];
|
|
1253
|
+
} else if (props.mode === 'dateTimeRange') {
|
|
1254
|
+
updateValue = selectedValue.value;
|
|
1255
|
+
}
|
|
1256
|
+
updateCalendarPage(updateValue);
|
|
1257
|
+
setHmsTime();
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
return {
|
|
1262
|
+
clickPrevNextBtn,
|
|
1263
|
+
clickDate,
|
|
1264
|
+
clickHmsBtn,
|
|
1265
|
+
clickTime,
|
|
1266
|
+
wheelMonth,
|
|
1267
|
+
wheelTime,
|
|
1268
|
+
calendarEventName,
|
|
1269
|
+
onMousemoveDate,
|
|
1270
|
+
preventTimeEventType,
|
|
1271
|
+
};
|
|
1272
|
+
};
|