@yxhl/specter-pui-vtk 1.0.95 → 1.0.97

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yxhl/specter-pui-vtk",
3
- "version": "1.0.95",
3
+ "version": "1.0.97",
4
4
  "description": "雅心互联 Vue 3 + Vuetify3 组件库",
5
5
  "type": "module",
6
6
  "main": "./dist/specter-pui.umd.js",
@@ -0,0 +1,344 @@
1
+ <template>
2
+ <div ref="headerRef" class="panel-header" @keydown.esc.stop="closePicker">
3
+ <button type="button" class="panel-nav" aria-label="上一周期" @click="handleNavigate('prev')">‹</button>
4
+
5
+ <div class="panel-title">
6
+ <button type="button" class="title-trigger" :class="{ active: openPicker === 'year' }"
7
+ :aria-expanded="openPicker === 'year'" aria-label="选择年份" @click.stop="togglePicker('year')">
8
+ {{ year }}年
9
+ </button>
10
+ <button v-if="showMonth" type="button" class="title-trigger month-trigger"
11
+ :class="{ active: openPicker === 'month' }" :aria-expanded="openPicker === 'month'" aria-label="选择月份"
12
+ @click.stop="togglePicker('month')">
13
+ {{ month + 1 }}月
14
+ </button>
15
+ </div>
16
+
17
+ <button type="button" class="panel-nav" aria-label="下一周期" @click="handleNavigate('next')">›</button>
18
+
19
+ <div v-if="openPicker" class="panel-popover" @click.stop>
20
+ <template v-if="openPicker === 'year'">
21
+ <div class="picker-navigation">
22
+ <button type="button" class="picker-nav" aria-label="上一组年份" @click="shiftYearPage(-YEAR_PAGE_SIZE)">
23
+ ‹
24
+ </button>
25
+ <span class="picker-range">{{ yearPage[0] }} - {{ yearPage[yearPage.length - 1] }}</span>
26
+ <button type="button" class="picker-nav" :disabled="!canShowNextYearPage" aria-label="下一组年份"
27
+ @click="shiftYearPage(YEAR_PAGE_SIZE)">
28
+ ›
29
+ </button>
30
+ </div>
31
+ <div class="picker-grid year-picker-grid">
32
+ <button v-for="item in yearPage" :key="item" type="button" class="picker-option"
33
+ :class="{ selected: item === year }" :disabled="isYearDisabled(item)" @click="selectYear(item)">
34
+ {{ item }}
35
+ </button>
36
+ </div>
37
+ </template>
38
+
39
+ <template v-else>
40
+ <div class="picker-caption">{{ year }}年</div>
41
+ <div class="picker-grid month-picker-grid">
42
+ <button v-for="(item, index) in MONTHS" :key="item" type="button" class="picker-option"
43
+ :class="{ selected: index === month }" :disabled="isMonthDisabled(index)" @click="selectMonth(index)">
44
+ {{ item }}
45
+ </button>
46
+ </div>
47
+ </template>
48
+ </div>
49
+ </div>
50
+ </template>
51
+
52
+ <script setup>
53
+ import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
54
+
55
+ defineOptions({
56
+ name: 'VtkDatePanelHeader',
57
+ });
58
+
59
+ const props = defineProps({
60
+ year: {
61
+ type: Number,
62
+ required: true
63
+ },
64
+ month: {
65
+ type: Number,
66
+ default: 0
67
+ },
68
+ showMonth: {
69
+ type: Boolean,
70
+ default: true
71
+ },
72
+ maxYear: {
73
+ type: Number,
74
+ default: null
75
+ },
76
+ maxMonth: {
77
+ type: Number,
78
+ default: 11
79
+ }
80
+ });
81
+
82
+ const emit = defineEmits(['update:year', 'update:month', 'prev', 'next']);
83
+
84
+ const YEAR_PAGE_SIZE = 12;
85
+ const YEAR_PAGE_CENTER_OFFSET = 4;
86
+ const MONTHS = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
87
+
88
+ const getYearPageStart = (year) => year - YEAR_PAGE_CENTER_OFFSET;
89
+
90
+ const openPicker = ref(null);
91
+ const yearPageStart = ref(getYearPageStart(props.year));
92
+ const headerRef = ref(null);
93
+
94
+ const yearPage = computed(() =>
95
+ Array.from({ length: YEAR_PAGE_SIZE }, (_, index) => yearPageStart.value + index)
96
+ );
97
+
98
+ const canShowNextYearPage = computed(() =>
99
+ props.maxYear === null || yearPageStart.value + YEAR_PAGE_SIZE <= props.maxYear
100
+ );
101
+
102
+ const isYearDisabled = (year) => props.maxYear !== null && year > props.maxYear;
103
+
104
+ const isMonthDisabled = (month) => {
105
+ if (props.maxYear === null || props.year < props.maxYear) return false;
106
+ if (props.year > props.maxYear) return true;
107
+ return month > props.maxMonth;
108
+ };
109
+
110
+ const togglePicker = (type) => {
111
+ if (openPicker.value === type) {
112
+ closePicker();
113
+ return;
114
+ }
115
+
116
+ if (type === 'year') {
117
+ yearPageStart.value = getYearPageStart(props.year);
118
+ }
119
+ openPicker.value = type;
120
+ };
121
+
122
+ const closePicker = () => {
123
+ openPicker.value = null;
124
+ };
125
+
126
+ const shiftYearPage = (offset) => {
127
+ const nextStart = yearPageStart.value + offset;
128
+ if (offset > 0 && props.maxYear !== null && nextStart > props.maxYear) return;
129
+ yearPageStart.value = nextStart;
130
+ };
131
+
132
+ const selectYear = (year) => {
133
+ if (isYearDisabled(year)) return;
134
+
135
+ emit('update:year', year);
136
+ if (props.showMonth) {
137
+ if (props.maxYear !== null && year === props.maxYear && props.month > props.maxMonth) {
138
+ emit('update:month', props.maxMonth);
139
+ }
140
+ openPicker.value = 'month';
141
+ } else {
142
+ closePicker();
143
+ }
144
+ };
145
+
146
+ const selectMonth = (month) => {
147
+ if (isMonthDisabled(month)) return;
148
+ emit('update:month', month);
149
+ closePicker();
150
+ };
151
+
152
+ const handleNavigate = (direction) => {
153
+ closePicker();
154
+ emit(direction);
155
+ };
156
+
157
+ const handleOutsideClick = (event) => {
158
+ if (openPicker.value && !headerRef.value?.contains(event.target)) {
159
+ closePicker();
160
+ }
161
+ };
162
+
163
+ watch(() => props.year, (year) => {
164
+ if (openPicker.value !== 'year') {
165
+ yearPageStart.value = getYearPageStart(year);
166
+ }
167
+ });
168
+
169
+ onMounted(() => document.addEventListener('click', handleOutsideClick));
170
+ onBeforeUnmount(() => document.removeEventListener('click', handleOutsideClick));
171
+ </script>
172
+
173
+ <style scoped>
174
+ .panel-header {
175
+ position: relative;
176
+ display: flex;
177
+ align-items: center;
178
+ justify-content: space-between;
179
+ min-height: 30px;
180
+ margin-bottom: 4px;
181
+ }
182
+
183
+ .panel-title {
184
+ display: flex;
185
+ align-items: center;
186
+ justify-content: center;
187
+ min-width: 112px;
188
+ }
189
+
190
+ .title-trigger,
191
+ .panel-nav,
192
+ .picker-nav,
193
+ .picker-option {
194
+ border: 0;
195
+ outline: none;
196
+ font: inherit;
197
+ }
198
+
199
+ .title-trigger {
200
+ position: relative;
201
+ padding: 5px 17px 5px 7px;
202
+ border-radius: 5px;
203
+ background: transparent;
204
+ color: rgb(var(--v-theme-on-surface));
205
+ font-size: 12px;
206
+ font-weight: 600;
207
+ line-height: 18px;
208
+ cursor: pointer;
209
+ transition: color 0.18s ease, background 0.18s ease;
210
+ }
211
+
212
+ .title-trigger::after {
213
+ position: absolute;
214
+ top: 10px;
215
+ right: 7px;
216
+ width: 5px;
217
+ height: 5px;
218
+ border-right: 1.5px solid currentColor;
219
+ border-bottom: 1.5px solid currentColor;
220
+ content: '';
221
+ transform: rotate(45deg);
222
+ transition: transform 0.18s ease;
223
+ }
224
+
225
+ .title-trigger:hover,
226
+ .title-trigger.active {
227
+ background: rgb(var(--v-theme-primary), 0.1);
228
+ color: rgb(var(--v-theme-primary));
229
+ }
230
+
231
+ .title-trigger.active::after {
232
+ top: 12px;
233
+ transform: rotate(225deg);
234
+ }
235
+
236
+ .month-trigger {
237
+ margin-left: 1px;
238
+ }
239
+
240
+ .panel-nav,
241
+ .picker-nav {
242
+ display: inline-flex;
243
+ align-items: center;
244
+ justify-content: center;
245
+ border-radius: 5px;
246
+ background: transparent;
247
+ color: rgb(var(--v-theme-on-surface), 0.55);
248
+ cursor: pointer;
249
+ transition: color 0.18s ease, background 0.18s ease;
250
+ }
251
+
252
+ .panel-nav {
253
+ width: 24px;
254
+ height: 24px;
255
+ font-size: 17px;
256
+ }
257
+
258
+ .panel-nav:hover,
259
+ .picker-nav:hover:not(:disabled) {
260
+ background: rgb(var(--v-theme-on-surface), 0.08);
261
+ color: rgb(var(--v-theme-on-surface));
262
+ }
263
+
264
+ .panel-popover {
265
+ position: absolute;
266
+ z-index: 5;
267
+ top: 34px;
268
+ left: 50%;
269
+ width: 204px;
270
+ padding: 10px;
271
+ border: 1px solid rgb(var(--v-theme-on-surface), 0.12);
272
+ border-radius: 8px;
273
+ background: rgb(var(--v-theme-surface));
274
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.16);
275
+ transform: translateX(-50%);
276
+ }
277
+
278
+ .picker-navigation {
279
+ display: flex;
280
+ align-items: center;
281
+ justify-content: space-between;
282
+ margin-bottom: 8px;
283
+ }
284
+
285
+ .picker-range,
286
+ .picker-caption {
287
+ color: rgb(var(--v-theme-on-surface), 0.72);
288
+ font-size: 12px;
289
+ font-weight: 600;
290
+ }
291
+
292
+ .picker-caption {
293
+ margin-bottom: 8px;
294
+ text-align: center;
295
+ }
296
+
297
+ .picker-nav {
298
+ width: 24px;
299
+ height: 24px;
300
+ font-size: 16px;
301
+ }
302
+
303
+ .picker-nav:disabled {
304
+ color: rgb(var(--v-theme-on-surface), 0.22);
305
+ cursor: not-allowed;
306
+ }
307
+
308
+ .picker-grid {
309
+ display: grid;
310
+ gap: 6px;
311
+ }
312
+
313
+ .year-picker-grid,
314
+ .month-picker-grid {
315
+ grid-template-columns: repeat(4, 1fr);
316
+ }
317
+
318
+ .picker-option {
319
+ min-width: 0;
320
+ padding: 7px 2px;
321
+ border-radius: 5px;
322
+ background: transparent;
323
+ color: rgb(var(--v-theme-on-surface), 0.82);
324
+ font-size: 12px;
325
+ cursor: pointer;
326
+ transition: color 0.18s ease, background 0.18s ease;
327
+ }
328
+
329
+ .picker-option:hover:not(:disabled) {
330
+ background: rgb(var(--v-theme-primary), 0.1);
331
+ color: rgb(var(--v-theme-primary));
332
+ }
333
+
334
+ .picker-option.selected {
335
+ background: rgb(var(--v-theme-primary));
336
+ color: rgb(var(--v-theme-on-primary));
337
+ font-weight: 600;
338
+ }
339
+
340
+ .picker-option:disabled {
341
+ color: rgb(var(--v-theme-on-surface), 0.24);
342
+ cursor: not-allowed;
343
+ }
344
+ </style>
@@ -57,11 +57,8 @@
57
57
  <div v-if="mode === 'range'" class="dual-calendar">
58
58
  <!-- 左侧年份 -->
59
59
  <div class="calendar-panel">
60
- <div class="calendar-header">
61
- <button class="nav-btn-small" @click="prevYear">‹</button>
62
- <span class="month-title">{{ currentYear }}年</span>
63
- <button class="nav-btn-small" @click="nextYearLeft">›</button>
64
- </div>
60
+ <VtkDatePanelHeader v-model:year="currentYear" :show-month="false"
61
+ :max-year="disableFuture ? todayYear : null" @prev="prevYear" @next="nextYearLeft" />
65
62
  <div class="month-grid">
66
63
  <div v-for="(month, index) in monthList" :key="index"
67
64
  :class="['month-item', { 'selected': isMonthSelected(index), 'in-range': isMonthInRange(index), 'disabled': isFutureMonth(index, currentYear) }]"
@@ -77,11 +74,8 @@
77
74
 
78
75
  <!-- 右侧年份 -->
79
76
  <div class="calendar-panel">
80
- <div class="calendar-header">
81
- <button class="nav-btn-small" @click="prevYearRight">‹</button>
82
- <span class="month-title">{{ rightYear }}年</span>
83
- <button class="nav-btn-small" @click="nextYear">›</button>
84
- </div>
77
+ <VtkDatePanelHeader v-model:year="rightYear" :show-month="false"
78
+ :max-year="disableFuture ? todayYear : null" @prev="prevYearRight" @next="nextYear" />
85
79
  <div class="month-grid">
86
80
  <div v-for="(month, index) in monthList" :key="index"
87
81
  :class="['month-item', { 'selected': isMonthSelectedRight(index), 'in-range': isMonthInRangeRight(index), 'disabled': isFutureMonth(index, rightYear) }]"
@@ -129,11 +123,9 @@
129
123
  <div v-if="mode === 'range'" class="dual-calendar">
130
124
  <!-- 左侧月份 -->
131
125
  <div class="calendar-panel">
132
- <div class="calendar-header">
133
- <button class="nav-btn-small" @click="prevMonth">‹</button>
134
- <span class="month-title">{{ currentYear }}年{{ currentMonth + 1 }}月</span>
135
- <button class="nav-btn-small" @click="nextMonthLeft">›</button>
136
- </div>
126
+ <VtkDatePanelHeader v-model:year="currentYear" v-model:month="currentMonth"
127
+ :max-year="disableFuture ? todayYear : null" :max-month="todayMonth" @prev="prevMonth"
128
+ @next="nextMonthLeft" />
137
129
  <div class="week-header">
138
130
  <div v-for="day in weekDays" :key="day" class="week-day">{{ day }}</div>
139
131
  </div>
@@ -158,11 +150,9 @@
158
150
 
159
151
  <!-- 右侧月份 -->
160
152
  <div class="calendar-panel">
161
- <div class="calendar-header">
162
- <button class="nav-btn-small" @click="prevMonthRight">‹</button>
163
- <span class="month-title">{{ nextMonthYear }}年{{ nextMonthMonth + 1 }}月</span>
164
- <button class="nav-btn-small" @click="nextMonth">›</button>
165
- </div>
153
+ <VtkDatePanelHeader v-model:year="rightYear" v-model:month="rightMonth"
154
+ :max-year="disableFuture ? todayYear : null" :max-month="todayMonth" @prev="prevMonthRight"
155
+ @next="nextMonth" />
166
156
  <div class="week-header">
167
157
  <div v-for="day in weekDays" :key="day" class="week-day">{{ day }}</div>
168
158
  </div>
@@ -253,11 +243,8 @@
253
243
  <div v-if="mode === 'range'" class="dual-calendar">
254
244
  <!-- 左侧年份 -->
255
245
  <div class="calendar-panel">
256
- <div class="calendar-header">
257
- <button class="nav-btn-small" @click="prevYear">‹</button>
258
- <span class="month-title">{{ currentYear }}年</span>
259
- <button class="nav-btn-small" @click="nextYearLeft">›</button>
260
- </div>
246
+ <VtkDatePanelHeader v-model:year="currentYear" :show-month="false"
247
+ :max-year="disableFuture ? todayYear : null" @prev="prevYear" @next="nextYearLeft" />
261
248
  <div class="month-grid">
262
249
  <div v-for="(month, index) in monthList" :key="index"
263
250
  :class="['month-item', { 'selected': isMonthSelected(index), 'in-range': isMonthInRange(index), 'disabled': isFutureMonth(index, currentYear) }]"
@@ -273,11 +260,8 @@
273
260
 
274
261
  <!-- 右侧年份 -->
275
262
  <div class="calendar-panel">
276
- <div class="calendar-header">
277
- <button class="nav-btn-small" @click="prevYearRight">‹</button>
278
- <span class="month-title">{{ rightYear }}年</span>
279
- <button class="nav-btn-small" @click="nextYear">›</button>
280
- </div>
263
+ <VtkDatePanelHeader v-model:year="rightYear" :show-month="false"
264
+ :max-year="disableFuture ? todayYear : null" @prev="prevYearRight" @next="nextYear" />
281
265
  <div class="month-grid">
282
266
  <div v-for="(month, index) in monthList" :key="index"
283
267
  :class="['month-item', { 'selected': isMonthSelectedRight(index), 'in-range': isMonthInRangeRight(index), 'disabled': isFutureMonth(index, rightYear) }]"
@@ -325,11 +309,9 @@
325
309
  <div v-if="mode === 'range'" class="dual-calendar">
326
310
  <!-- 左侧月份 -->
327
311
  <div class="calendar-panel">
328
- <div class="calendar-header">
329
- <button class="nav-btn-small" @click="prevMonth">‹</button>
330
- <span class="month-title">{{ currentYear }}年{{ currentMonth + 1 }}月</span>
331
- <button class="nav-btn-small" @click="nextMonthLeft">›</button>
332
- </div>
312
+ <VtkDatePanelHeader v-model:year="currentYear" v-model:month="currentMonth"
313
+ :max-year="disableFuture ? todayYear : null" :max-month="todayMonth" @prev="prevMonth"
314
+ @next="nextMonthLeft" />
333
315
  <div class="week-header">
334
316
  <div v-for="day in weekDays" :key="day" class="week-day">{{ day }}</div>
335
317
  </div>
@@ -354,11 +336,9 @@
354
336
 
355
337
  <!-- 右侧月份 -->
356
338
  <div class="calendar-panel">
357
- <div class="calendar-header">
358
- <button class="nav-btn-small" @click="prevMonthRight">‹</button>
359
- <span class="month-title">{{ nextMonthYear }}年{{ nextMonthMonth + 1 }}月</span>
360
- <button class="nav-btn-small" @click="nextMonth">›</button>
361
- </div>
339
+ <VtkDatePanelHeader v-model:year="rightYear" v-model:month="rightMonth"
340
+ :max-year="disableFuture ? todayYear : null" :max-month="todayMonth" @prev="prevMonthRight"
341
+ @next="nextMonth" />
362
342
  <div class="week-header">
363
343
  <div v-for="day in weekDays" :key="day" class="week-day">{{ day }}</div>
364
344
  </div>
@@ -421,8 +401,9 @@
421
401
  </div>
422
402
  </template>
423
403
 
424
- <script setup>
425
- import { ref, computed, watch } from 'vue';
404
+ <script setup>
405
+ import { ref, computed, watch } from 'vue';
406
+ import VtkDatePanelHeader from './VtkDatePanelHeader.vue';
426
407
 
427
408
  // 定义组件名称
428
409
  defineOptions({
@@ -598,14 +579,14 @@ const formatDisplay = (value) => {
598
579
  };
599
580
 
600
581
  // 年份列表(显示12年)
601
- const yearList = computed(() => {
602
- // 年份面板打开时,以当前锚点年份为中心生成 12 个年份选项
603
- const startYear = currentYear.value - YEAR_PANEL_CENTER_OFFSET;
604
- return Array.from({ length: YEAR_PANEL_SIZE }, (_, i) => startYear + i);
605
- });
606
-
607
- // 周列表
608
- const weekList = computed(() => {
582
+ const yearList = computed(() => {
583
+ // 年份面板打开时,以当前锚点年份为中心生成 12 个年份选项
584
+ const startYear = currentYear.value - YEAR_PANEL_CENTER_OFFSET;
585
+ return Array.from({ length: YEAR_PANEL_SIZE }, (_, i) => startYear + i);
586
+ });
587
+
588
+ // 周列表
589
+ const weekList = computed(() => {
609
590
  const weeks = [];
610
591
  const firstDay = new Date(currentYear.value, currentMonth.value, 1);
611
592
  const lastDay = new Date(currentYear.value, currentMonth.value + 1, 0);
@@ -1095,55 +1076,76 @@ const changeViewMode = (mode) => {
1095
1076
  viewMode.value = mode;
1096
1077
  };
1097
1078
 
1098
- // 面板操作
1099
- const initializePicker = () => {
1100
- if (props.displayMode === 'year' && props.mode === 'single') {
1101
- currentYear.value = todayYear;
1102
- }
1103
-
1104
- tempValue.value = selectedValue.value ?
1105
- (Array.isArray(selectedValue.value) ? [...selectedValue.value] : selectedValue.value) :
1106
- (props.mode === 'range' ? [] : null);
1107
- viewMode.value = props.displayMode;
1108
-
1109
- if (props.displayMode === 'month') {
1110
- rightYear.value = currentYear.value + 1;
1111
- } else {
1112
- if (currentMonth.value === 11) {
1113
- rightYear.value = currentYear.value + 1;
1114
- rightMonth.value = 0;
1115
- } else {
1116
- rightYear.value = currentYear.value;
1117
- rightMonth.value = currentMonth.value + 1;
1118
- }
1119
- }
1120
- };
1121
-
1122
- watch(showPicker, (isOpen) => {
1079
+ // 面板操作
1080
+ const parseCalendarValue = (value) => {
1081
+ if (typeof value !== 'string') return null;
1082
+
1083
+ const match = value.match(/^(\d{4})(?:-(\d{2}))?/);
1084
+ if (!match) return null;
1085
+
1086
+ const month = match[2] ? Number(match[2]) - 1 : null;
1087
+ return {
1088
+ year: Number(match[1]),
1089
+ month: month !== null && month >= 0 && month <= 11 ? month : null
1090
+ };
1091
+ };
1092
+
1093
+ const setRightPanelToNextMonth = () => {
1094
+ if (currentMonth.value === 11) {
1095
+ rightYear.value = currentYear.value + 1;
1096
+ rightMonth.value = 0;
1097
+ } else {
1098
+ rightYear.value = currentYear.value;
1099
+ rightMonth.value = currentMonth.value + 1;
1100
+ }
1101
+ };
1102
+
1103
+ const syncCalendarPanels = () => {
1104
+ const values = Array.isArray(selectedValue.value) ? selectedValue.value : [selectedValue.value];
1105
+ const start = parseCalendarValue(values[0]);
1106
+ const end = parseCalendarValue(values[1]);
1107
+
1108
+ if (start) {
1109
+ currentYear.value = start.year;
1110
+ if (start.month !== null) currentMonth.value = start.month;
1111
+ }
1112
+
1113
+ if (props.displayMode === 'year') return;
1114
+
1115
+ if (props.displayMode === 'month') {
1116
+ rightYear.value = end && end.year !== currentYear.value ? end.year : currentYear.value + 1;
1117
+ return;
1118
+ }
1119
+
1120
+ const endUsesDifferentMonth = end && end.month !== null &&
1121
+ (end.year !== currentYear.value || end.month !== currentMonth.value);
1122
+
1123
+ if (props.mode === 'range' && endUsesDifferentMonth) {
1124
+ rightYear.value = end.year;
1125
+ rightMonth.value = end.month;
1126
+ } else {
1127
+ setRightPanelToNextMonth();
1128
+ }
1129
+ };
1130
+
1131
+ const initializePicker = () => {
1132
+ tempValue.value = selectedValue.value ?
1133
+ (Array.isArray(selectedValue.value) ? [...selectedValue.value] : selectedValue.value) :
1134
+ (props.mode === 'range' ? [] : null);
1135
+ viewMode.value = props.displayMode;
1136
+ syncCalendarPanels();
1137
+ };
1138
+
1139
+ watch(showPicker, (isOpen) => {
1123
1140
  if (isOpen) {
1124
- initializePicker();
1125
- }
1126
- });
1127
- if (props.inline) {
1128
- // 内联模式下直接初始化
1129
- tempValue.value = selectedValue.value ?
1130
- (Array.isArray(selectedValue.value) ? [...selectedValue.value] : selectedValue.value) :
1131
- (props.mode === 'range' ? [] : null);
1132
- viewMode.value = props.displayMode;
1133
-
1134
- // 初始化右侧日历/年份
1135
- if (props.displayMode === 'month') {
1136
- rightYear.value = currentYear.value + 1;
1137
- } else {
1138
- if (currentMonth.value === 11) {
1139
- rightYear.value = currentYear.value + 1;
1140
- rightMonth.value = 0;
1141
- } else {
1142
- rightYear.value = currentYear.value;
1143
- rightMonth.value = currentMonth.value + 1;
1144
- }
1145
- }
1146
- }
1141
+ initializePicker();
1142
+ }
1143
+ });
1144
+
1145
+ watch(() => props.modelValue, (newValue) => {
1146
+ selectedValue.value = newValue;
1147
+ if (props.inline) initializePicker();
1148
+ }, { immediate: true });
1147
1149
 
1148
1150
  const closePicker = () => {
1149
1151
  showPicker.value = false;
@@ -1181,11 +1183,7 @@ const handleConfirm = () => {
1181
1183
  }
1182
1184
  };
1183
1185
 
1184
- // 监听外部值变化
1185
- watch(() => props.modelValue, (newValue) => {
1186
- selectedValue.value = newValue;
1187
- }, { immediate: true });
1188
- </script>
1186
+ </script>
1189
1187
 
1190
1188
  <style scoped>
1191
1189
  .vtk-date-selector {
@@ -1320,42 +1318,8 @@ watch(() => props.modelValue, (newValue) => {
1320
1318
  max-width: 340px;
1321
1319
  }
1322
1320
 
1323
- .calendar-header {
1324
- display: flex;
1325
- align-items: center;
1326
- justify-content: space-between;
1327
- padding: 4px 0;
1328
- margin-bottom: 4px;
1329
- }
1330
-
1331
- .month-title {
1332
- font-size: 12px;
1333
- font-weight: 500;
1334
- color: rgb(var(--v-theme-on-surface));
1335
- }
1336
-
1337
- .nav-btn-small {
1338
- border: none;
1339
- background: none;
1340
- font-size: 14px;
1341
- color: rgb(var(--v-theme-on-surface), 0.6);
1342
- cursor: pointer;
1343
- padding: 2px 4px;
1344
- border-radius: 4px;
1345
- transition: background 0.2s;
1346
- }
1347
-
1348
- .nav-btn-small:hover {
1349
- background: rgb(var(--v-theme-on-surface), 0.08);
1350
- }
1351
-
1352
- .nav-btn-small.invisible {
1353
- visibility: hidden;
1354
- pointer-events: none;
1355
- }
1356
-
1357
- .year-grid,
1358
- .month-grid {
1321
+ .year-grid,
1322
+ .month-grid {
1359
1323
  display: grid;
1360
1324
  grid-template-columns: repeat(3, 1fr);
1361
1325
  gap: 6px;