@neatui/vue 3.1.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +1 -10
  2. package/package.json +5 -2
  3. package/rollup.config.js +42 -2
  4. package/src/components/basic/IDraggable@b.vue +1 -1
  5. package/src/components/basic/IFollowView.vue +2 -2
  6. package/src/components/basic/LayerView/Layer.vue +193 -42
  7. package/src/components/display/Calendar.vue +6 -6
  8. package/src/components/display/CalendarReg.vue +6 -6
  9. package/src/components/display/PhotoEditor.vue +2 -2
  10. package/src/components/display/TreeView.vue +4 -3
  11. package/src/components/form/Cascader.vue +3 -3
  12. package/src/components/form/DatePicker.vue +2 -2
  13. package/src/components/form/{DateRangePicker@v3.vue → DateRangePicker.vue} +2 -1
  14. package/src/components/form/{DateRangeView@v3.vue → DateRangeView.vue} +11 -11
  15. package/src/components/form/DateView.vue +461 -82
  16. package/src/components/form/ImgUpload.vue +3 -3
  17. package/src/components/form/{MoreSelect@v3.vue → MoreSelect.vue} +1 -1
  18. package/src/components/form/MoreSelectList.vue +1 -1
  19. package/src/components/form/{MoreSelectPanel@v3.vue → MoreSelectPanel.vue} +1 -1
  20. package/src/components/form/MoreSelectPicker.vue +3 -3
  21. package/src/components/form/MoreSelectTags.vue +1 -1
  22. package/src/components/form/PageMoreSelect.vue +1 -1
  23. package/src/components/form/PageSelect.vue +1 -1
  24. package/src/components/form/SearchMoreSelect.vue +1 -1
  25. package/src/components/form/{SearchSelect@v3.vue → SearchSelect.vue} +1 -1
  26. package/src/components/form/{Select@v3.vue → Select.vue} +1 -1
  27. package/src/components/form/SelectList.vue +1 -1
  28. package/src/components/form/SelectPicker.vue +3 -3
  29. package/src/components/form/SelectTree/{SelectTree@v1.vue → SelectTree.vue} +1 -1
  30. package/src/components/form/Switch.vue +2 -1
  31. package/src/components/form/TimeView.vue +6 -6
  32. package/src/components/form/Upload.vue +22 -11
  33. package/src/components/form/index.ts +8 -8
  34. package/src/components/loader/FormLoader/FormLoader.vue +124 -103
  35. package/src/components/loader/LayerLoader/LayerLoader.vue +6 -7
  36. package/src/components/loader/LimitLoader/{LimitLoader@v3.vue → LimitLoader.vue} +4 -4
  37. package/src/components/loader/LimitLoader/index.ts +1 -1
  38. package/src/components/loader/TableLoader/TableLoader.vue +7 -7
  39. package/src/components/loader/TableLoader/TableRender.vue +0 -1
  40. package/src/components/loader/ViewLoader/{ViewLoader@v1.vue → ViewLoader.vue} +124 -103
  41. package/src/components/loader/ViewLoader/index.ts +1 -1
  42. package/src/components/tools/FormDraftsView.vue +4 -4
  43. package/src/components/tools/FormVerifyView.vue +2 -2
  44. package/src/components/tools/MoreTools.vue +5 -5
  45. package/src/components/tools/Pagination@a.vue +4 -4
  46. package/src/components/tools/Pagination@b.vue +4 -4
  47. package/tsconfig.json +1 -2
  48. package/src/components/form/DateView@v2.vue +0 -386
  49. package/src/components/form/DateView@v3.vue +0 -608
  50. package/src/components/tools/MoreTools@v2.vue +0 -74
  51. /package/src/components/form/{Input@v3.vue → Input.vue} +0 -0
  52. /package/src/components/form/{TextArea.vue → Textarea.vue} +0 -0
@@ -1,34 +1,83 @@
1
1
  <template>
2
- <div ui-date="@a" class="w-ll" :class="`${frame ? 'b-solid bk-line b-xs r-sm' : ''}`" ui-flex="col xy">
3
- <div ui-date-head="" class="nx-sl mt-ss ny-sm b-solid bk-back" ui-flex="row xm">
2
+ <div ui-date="@a" class="fekit-date-view pr" :class="`${frame ? 'b-solid b-line b-xs r-sm' : ''}`" ui-flex="col xy" style="width: 22em">
3
+ <div ui-date-head="" class="flex-fixed nx-ms mt-ss ny-sm b-solid b-line" ui-flex="row xm">
4
4
  <div ui-flex="row lm" class="o-lm">
5
- <div ui-btn="@a s none :square" @click="prev('y')">
6
- <Icon name="double-arrow-left" />
7
- </div>
8
- <div ui-btn="@a s none :square" @click="prev('m')">
5
+ <div ui-btn="@a s none :square" @click="prev()">
9
6
  <Icon name="arrow-left" />
10
7
  </div>
11
8
  </div>
12
- <div ui-flex="row cm" class="nx-ss-sub">
13
- <div ui-flex="row cm" class="nx-ss-sub">
14
- <p class="mx-ss-sub">
15
- <b>{{ state.curr.y }}年</b>
16
- <b>{{ state.curr.m }}月</b>
17
- </p>
18
- </div>
9
+ <div ui-btns="@a none s" @click="setPanelType()">
10
+ <p v-if="state.type === 2" class="nowrap" ui-flex="row cm">
11
+ <b class="nx-ss">{{ panel.sta }}年</b>
12
+ <span class="nx-xs">-</span>
13
+ <b class="nx-ss">{{ panel.end }}年</b>
14
+ </p>
15
+ <p v-else-if="state.type === 1" class="nowrap" ui-flex="row cm">
16
+ <b class="nx-ss">{{ state.curr.y }}年</b>
17
+ </p>
18
+ <p v-else class="nowrap" ui-flex="row cm">
19
+ <b class="nx-xs">{{ state.curr.y }}年</b>
20
+ <b class="nx-xs">{{ state.curr.M }}月</b>
21
+ </p>
19
22
  </div>
20
23
  <div ui-flex="row rm" class="o-lm">
21
- <div ui-btn="@a s none :square" @click="next('m')">
24
+ <div ui-btn="@a s none :square" @click="next()">
22
25
  <Icon name="arrow-right" />
23
26
  </div>
24
- <div ui-btn="@a s none :square" @click="next('y')">
25
- <Icon name="double-arrow-right" />
26
- </div>
27
27
  </div>
28
28
  </div>
29
- <div ui-date-body="">
30
- <div class="nb-sm">
31
- <ul ui-flex="row am" class="fs-xs nx-sm nt-sm">
29
+ <div ui-date-body="" :class="['flex-block pr', bodyClass]">
30
+ <div v-if="state.type === 2" class="full">
31
+ <ul ui-grid="mob:4" class="n-ms ny-sm-sub">
32
+ <li v-for="(year, idx) in panel.years" :key="idx" ui-flex="col cm" @click="setDateValue('y', year)">
33
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedYear(year) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">
34
+ {{ year }}
35
+ </div>
36
+ </li>
37
+ </ul>
38
+ </div>
39
+ <div v-else-if="state.type === 1" class="full">
40
+ <ul ui-grid="mob:4" class="n-ms ny-sm-sub">
41
+ <li ui-flex="col cm" @click="setDateValue('m', 1)">
42
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(1) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">1月</div>
43
+ </li>
44
+ <li ui-flex="col cm" @click="setDateValue('m', 2)">
45
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(2) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">2月</div>
46
+ </li>
47
+ <li ui-flex="col cm" @click="setDateValue('m', 3)">
48
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(3) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">3月</div>
49
+ </li>
50
+ <li ui-flex="col cm" @click="setDateValue('m', 4)">
51
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(4) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">4月</div>
52
+ </li>
53
+ <li ui-flex="col cm" @click="setDateValue('m', 5)">
54
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(5) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">5月</div>
55
+ </li>
56
+ <li ui-flex="col cm" @click="setDateValue('m', 6)">
57
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(6) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">6月</div>
58
+ </li>
59
+ <li ui-flex="col cm" @click="setDateValue('m', 7)">
60
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(7) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">7月</div>
61
+ </li>
62
+ <li ui-flex="col cm" @click="setDateValue('m', 8)">
63
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(8) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">8月</div>
64
+ </li>
65
+ <li ui-flex="col cm" @click="setDateValue('m', 9)">
66
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(9) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">9月</div>
67
+ </li>
68
+ <li ui-flex="col cm" @click="setDateValue('m', 10)">
69
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(10) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">10月</div>
70
+ </li>
71
+ <li ui-flex="col cm" @click="setDateValue('m', 11)">
72
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(11) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">11月</div>
73
+ </li>
74
+ <li ui-flex="col cm" @click="setDateValue('m', 12)">
75
+ <div ui-flex="col cm" class="w-ls h-sl ac ux-none r-sm" :class="isSelectedMonth(12) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">12月</div>
76
+ </li>
77
+ </ul>
78
+ </div>
79
+ <div v-else class="full">
80
+ <ul ui-flex="row am" class="fs-ss nx-sm nt-ss">
32
81
  <li class="flex-block ac"><span>日</span></li>
33
82
  <li class="flex-block ac"><span>一</span></li>
34
83
  <li class="flex-block ac"><span>二</span></li>
@@ -47,52 +96,129 @@
47
96
  :class="`${item.range ? 'bg-main-o-xs' : ''} ${item.sta ? 'r-lt-sl r-lb-sl' : ''} ${item.end ? 'r-rt-sl r-rb-sl' : ''}`"
48
97
  ui-flex="col cm"
49
98
  @click="change(item)"
50
- @mouseenter="setRangeValue(item)"
51
99
  >
52
100
  <div
53
101
  ui-flex="col cm"
54
- class="r-sm b-solid bk-none b-xs my-ss ux-scale"
102
+ class="r-sm b-solid b-none b-xs my-ss ux-scale"
55
103
  style="width: 1.8em; height: 1.8em"
56
- :class="`${item.isLastMonth || item.isNextMonth ? 'co-idle' : item.isHoliday ? 'co-risk' : ''} ${item.selected ? 'active bg-main-o-lm co-fore' : 'hover-bg-weak'}`"
104
+ :class="`${item.isLastMonth || item.isNextMonth ? 'co-idle' : item.isHoliday ? 'co-risk' : ''} ${item.selected ? 'active bg-main-o-lm co-w' : 'bg-weak:hover co-main:hover'}`"
57
105
  >
58
106
  <span>{{ item.day }}</span>
59
107
  </div>
60
108
  </div>
61
- <div class="pa w-full lh-xs ny-ss ob-no ol-no nowrap s-ml bold" style="transform-origin: 50% 110%" ui-flex="row ct" v-html="extras(item)"></div>
109
+ <div class="pa w-full lh-xs ny-ss ob-no ol-no nowrap s-ml bold" style="transform-origin: 50% 100%" ui-flex="row ct" v-html="extras(item)"></div>
62
110
  </div>
63
111
  </li>
64
112
  </ul>
65
113
  </div>
66
114
  </div>
67
- <div v-if="tools" ui-date-foot="" ui-flex="row xm" class="nx-sl ny-sl b-solid bk-line-o-mm bt-xs">
115
+ <template v-if="!state.isNoTime">
116
+ <div v-if="state.type === 0" class="h-ls ny-sm nx-ms">
117
+ <div class="full ac b-solid b-line b-xs r-sm" ui-row="mob-8" style="width: 100%; height: 3em; padding: 0.5em">
118
+ <ul
119
+ :style="`${state.panel ? '' : 'pointer-events: none; opacity:.35'}`"
120
+ class="fekit-date-view-time-scroll"
121
+ ref="ths"
122
+ ui-scroll=":y xs"
123
+ ui-btns="@a none s :block"
124
+ data-type="h"
125
+ @scroll="timeScroll"
126
+ >
127
+ <li v-for="(item, idx) in hours" :key="idx">
128
+ {{ item }}
129
+ </li>
130
+ </ul>
131
+ <ul
132
+ :style="`${state.panel ? '' : 'pointer-events: none; opacity:.35'}`"
133
+ class="fekit-date-view-time-scroll"
134
+ ref="tms"
135
+ ui-scroll=":y xs"
136
+ ui-btns="@a none s :block"
137
+ data-type="m"
138
+ @scroll="timeScroll"
139
+ >
140
+ <li v-for="(item, idx) in seconds" :key="idx">
141
+ {{ item }}
142
+ </li>
143
+ </ul>
144
+ <ul
145
+ :style="`${state.panel ? '' : 'pointer-events: none; opacity:.35'}`"
146
+ class="fekit-date-view-time-scroll"
147
+ ref="tss"
148
+ ui-scroll=":y xs"
149
+ ui-btns="@a none s :block"
150
+ data-type="s"
151
+ @scroll="timeScroll"
152
+ >
153
+ <li v-for="(item, idx) in seconds" :key="idx">
154
+ {{ item }}
155
+ </li>
156
+ </ul>
157
+ </div>
158
+ </div>
159
+ <div v-else class="h-ls"></div>
160
+ </template>
161
+ <div v-if="showTools" ui-date-foot="" ui-flex="row xm" class="flex-fixed nx-sl ny-sl b-solid b-line bt-xs">
68
162
  <div ui-flex="row lm" class="nl-ss">
69
- <p v-if="state.date">
70
- <span class="nowrap">已选:</span>
71
- <span>{{ state.value }}</span>
72
- </p>
163
+ <div v-if="state.panel" ui-flex="row lm">
164
+ <span :anim-editing="state.type === baseType" class="nx-ss">{{ idate(state.panel).format(panelFormat) }}</span>
165
+ <!-- <span v-if="!state.isNoTime" :anim-editing="state.type === baseType" class="nx-ss">{{ idate(state.panel).format('HH:mm:ss') }}</span> -->
166
+ </div>
73
167
  <div v-else-if="state.shortcuts?.length" ui-flex="row lm">
74
- <span class="nowrap">推荐:</span>
75
- <div class="mr-sm-sub nx-sl-sub ny-ss-sub lh-ss nowrap fs-ss dib-sub" ui-scroll=":x y:hidden">
76
- <p v-for="(item, idx) in state.shortcuts" :key="idx" class="bg-main-o-xs co-main r-xl" ui-btn="@a xs none :round" @click="item?.func">{{ item?.text }}</p>
77
- <p class="bg-main-o-xs co-main r-xl" ui-btn="@a xs none :round"><Icon class="co-main" name="more" /></p>
168
+ <div class="mr-sm-sub nx-sl-sub ny-ss-sub lh-ss nowrap fs-sm dib-sub" ui-scroll=":x y:hidden">
169
+ <p v-for="(item, idx) in state.shortcuts" :key="idx" class="bg-main-o-ss co-main r-xl" ui-btn="@a xs none :round" @click="item?.func">{{ item?.text }}</p>
170
+ <p class="bg-main-o-ss co-main r-xl" ui-btn="@a xs none :round"><Icon class="co-main" name="more" /></p>
78
171
  </div>
79
172
  </div>
80
- <div v-else>&nbsp;</div>
173
+ <div>&nbsp;</div>
81
174
  </div>
82
- <div>
83
- <button v-if="state.value" ui-btn="@a xs none" class="co-risk" @click="change('')">清空</button>
175
+ <div class="ml-ss-sub" ui-flex="row rm">
176
+ <button v-if="state.panel" ui-btn="@a xs none" class="nx-ss co-risk" @click="remove">清除</button>
177
+ <button v-if="state.panel && !state.isNoTime" ui-btn="@a xs none" class="nx-ss co-main" @click="update">确定</button>
84
178
  </div>
85
179
  </div>
86
180
  </div>
87
181
  </template>
88
182
  <script setup lang="ts">
89
- import { computed, reactive, watch } from 'vue';
90
- import { idate, isDateString, isString } from '@fekit/utils';
183
+ import { computed, ref, reactive, watch, onMounted } from 'vue';
184
+ import { idate, isDateString, isObject, isString } from '@fekit/utils';
91
185
  import { Icon } from '../basic';
92
186
 
187
+ const normalizeDateInput = (value: any) => {
188
+ if (!value) {
189
+ return value;
190
+ }
191
+ if (isString(value)) {
192
+ if (/^\d{4}$/.test(value)) {
193
+ return `${value}-01-01`;
194
+ }
195
+ if (/^\d{4}-(\d{1,2})$/.test(value)) {
196
+ const [year, month] = value.split('-');
197
+ const m = month.padStart(2, '0');
198
+ return `${year}-${m}-01`;
199
+ }
200
+ }
201
+ return value;
202
+ };
203
+
204
+ interface PickerConfig {
205
+ baseType: number;
206
+ format: string;
207
+ bodyClass: string;
208
+ tools: boolean;
209
+ }
210
+
211
+ const PICKER_CONFIGS: Record<string, PickerConfig> = {
212
+ date: { baseType: 0, format: 'YYYY-MM-DD', bodyClass: 'min-h-xs*m', tools: true },
213
+ month: { baseType: 1, format: 'YYYY-MM', bodyClass: 'min-h-ms*s', tools: false },
214
+ year: { baseType: 2, format: 'YYYY', bodyClass: 'min-h-ms*s', tools: false },
215
+ };
216
+
93
217
  // 创建日期
94
218
  const cDate = (date: any = null) => {
95
- return isDateString(date) ? new Date(date) : new Date();
219
+ const normalized = normalizeDateInput(date);
220
+ // iOS 兼容:Safari 不支持 "YYYY-MM-DD" 格式,需要替换为 "YYYY/MM/DD"
221
+ return isDateString(normalized) ? new Date(normalized?.replace(/-/g, '/')) : new Date();
96
222
  };
97
223
 
98
224
  // 快捷预设
@@ -117,18 +243,24 @@
117
243
  },
118
244
  };
119
245
 
120
- const emits: any = defineEmits(['update:modelValue', 'change']);
246
+ const emits: any = defineEmits(['update:modelValue', 'change', 'update']);
247
+ interface TimeProps {
248
+ h: boolean | number;
249
+ m?: boolean | number;
250
+ s?: boolean | number;
251
+ }
121
252
  interface Props {
122
253
  modelValue?: string | number;
123
254
  frame: boolean;
124
255
  tools?: boolean;
125
256
  range?: boolean;
126
257
  format?: string;
127
- picker?: 'date' | 'week' | 'month' | 'quarter';
258
+ picker?: 'date' | 'week' | 'month' | 'quarter' | 'year';
128
259
  holiday?: object;
129
260
  shortcuts?: Array<'yesterday' | 'today' | 'tomorrow'>;
130
261
  extras?: (item: any) => string;
131
- rangeValue?: string;
262
+ time?: boolean | TimeProps;
263
+ lang?: string;
132
264
  }
133
265
  const props: any = withDefaults(defineProps<Props>(), {
134
266
  // 值
@@ -136,94 +268,341 @@
136
268
  // 是否显示框架
137
269
  frame: false,
138
270
  // 是否是示工具
139
- tools: true,
271
+ tools: undefined,
140
272
  // 时间范围
141
273
  range: false,
142
274
  // 面板类型
143
275
  picker: 'date',
144
276
  // 额外内容
145
277
  extras: (item: any) => {
146
- return `<i class="dib o-ms mb-xs r-xl ${item.today ? 'bg-main' : 'bg-none'}" style="width:.6em; height:.6em;"></i>`;
278
+ return `<i class="dib o-ms mb-xs r-xl ${item.today ? 'bg-main' : 'bg-none'}" style="width:.5em; height:.5em;"></i>`;
147
279
  },
148
280
  // 快捷选择
149
281
  shortcuts: () => ['yesterday', 'today', 'tomorrow'],
150
282
  // 数据格式化
151
- format: 'YYYY-MM-DD',
283
+ format: undefined,
152
284
  // 节假日
153
285
  holiday: () => ({}),
154
- // 范围时间
155
- rangeValue: '',
286
+ time: false,
287
+ // 语言
288
+ lang: 'zh-CN',
289
+ });
290
+
291
+ const pickerConfig = computed<PickerConfig>(() => {
292
+ const key = props.picker || 'date';
293
+ return PICKER_CONFIGS[key] || PICKER_CONFIGS.date;
294
+ });
295
+
296
+ // 小时
297
+ const hours = computed(() => {
298
+ const _hours: any = [];
299
+ for (let i = 0; i < 24; i++) {
300
+ _hours.push(i > 9 ? `${i}` : `0${i}`);
301
+ }
302
+ return _hours;
303
+ });
304
+ // 分秒
305
+ const seconds = computed(() => {
306
+ const _seconds: any = [];
307
+ for (let i = 0; i < 60; i++) {
308
+ _seconds.push(i > 9 ? `${i}` : `0${i}`);
309
+ }
310
+ return _seconds;
311
+ });
312
+
313
+ const baseType = computed(() => pickerConfig.value.baseType);
314
+
315
+ const panelFormat = computed(() => props.format || pickerConfig.value.format);
316
+
317
+ const bodyClass = computed(() => pickerConfig.value.bodyClass);
318
+
319
+ const showTools = computed(() => {
320
+ if (props.tools !== undefined) {
321
+ return !!props.tools;
322
+ }
323
+ return pickerConfig.value.tools;
156
324
  });
157
325
 
326
+ const isSelectedYear = (year: number) => {
327
+ return !!state.panel && state.curr?.y === year;
328
+ };
329
+
330
+ const isSelectedMonth = (month: number) => {
331
+ return !!state.panel && state.curr?.M === month;
332
+ };
333
+
158
334
  // 内部数据
159
335
  const state: any = reactive({
160
- date: '',
336
+ // 面板显示时间
337
+ panel: undefined,
338
+ // 面板年份选择
339
+ years: 0,
161
340
  // 当前时间计算
162
341
  curr: computed(() => {
163
- const date = idate(state.date);
164
- const days = date.calendar({ group: true, value: [state.value] });
165
- const { y = 0, M: m = 0, d = 0 } = date.attr || {};
166
- return { days, y, m, d };
342
+ const date = idate(state.panel);
343
+ const days = date.calendar({ group: true, value: [state.panel] });
344
+ return { days, ...date.attr };
167
345
  }),
346
+ // 当前面板类型
347
+ type: 0,
168
348
  // 值
169
- value: '',
170
- // 范围时间值
171
- rangeValue: '',
349
+ value: undefined,
172
350
  // 快捷选择
173
351
  shortcuts: computed(() => {
174
352
  return props.shortcuts.map((item: any) => {
175
353
  return isString(item) ? shortcutsList[item] : item;
176
354
  });
177
355
  }),
356
+ // 不带时间(只要时或分的也考虑到)
357
+ isNoTime: computed(() => {
358
+ if (isObject(props.time)) {
359
+ return !(props.time.h || props.time.m || props.time.s);
360
+ } else {
361
+ return !props.time;
362
+ }
363
+ }),
178
364
  });
179
365
 
366
+ watch(
367
+ () => baseType.value,
368
+ (value) => {
369
+ state.type = value;
370
+ },
371
+ { immediate: true },
372
+ );
373
+
374
+ watch(
375
+ () => panelFormat.value,
376
+ (format) => {
377
+ if (state.panel) {
378
+ const formatted = idate(state.panel).format(format);
379
+ if (formatted !== state.panel) {
380
+ state.panel = formatted;
381
+ }
382
+ state.value = formatted;
383
+ }
384
+ },
385
+ { immediate: true },
386
+ );
387
+
388
+ // 面板
389
+ const panel: any = reactive({
390
+ years: computed(() => {
391
+ const startYear = Math.floor(state.curr.year / 10) * 10;
392
+ const list = [];
393
+ for (let i = 0; i < 10; i++) {
394
+ list.push(startYear + i);
395
+ }
396
+ return list;
397
+ }),
398
+ sta: computed(() => {
399
+ return panel.years[0];
400
+ }),
401
+ end: computed(() => {
402
+ return panel.years[9];
403
+ }),
404
+ });
405
+
406
+ // 设置日期
407
+ const setDateValue = (type: any, value: number = 0, add: number = 0) => {
408
+ const panelValue = upload(type, value, add);
409
+ state.panel = panelValue;
410
+ state.value = panelValue;
411
+ // 面板返回上一级
412
+ const base = baseType.value;
413
+ const nextType = Math.max(base, state.type - 1);
414
+ state.type = nextType;
415
+
416
+ if (base > 0 && nextType === base) {
417
+ emits('change', panelValue);
418
+ update();
419
+ }
420
+ };
421
+
422
+ // 时分秒
423
+ const ths: any = ref(null);
424
+ const tms: any = ref(null);
425
+ const tss: any = ref(null);
426
+ const setTimeValue = () => {
427
+ const { H = 0, m = 0, s = 0 }: any = state.curr || {};
428
+ if (state.panel) {
429
+ if (ths.value && ths.value.children[H]) {
430
+ ths.value.scrollTop = ths.value.children[H].offsetTop;
431
+ }
432
+ if (tms.value && tms.value.children[m]) {
433
+ tms.value.scrollTop = tms.value.children[m].offsetTop;
434
+ }
435
+ if (tss.value && tss.value.children[s]) {
436
+ tss.value.scrollTop = tss.value.children[s].offsetTop;
437
+ }
438
+ } else {
439
+ if (ths.value && ths.value.children[H]) {
440
+ ths.value.scrollTop = ths.value.children[0].offsetTop;
441
+ }
442
+ if (tms.value && tms.value.children[m]) {
443
+ tms.value.scrollTop = tms.value.children[0].offsetTop;
444
+ }
445
+ if (tss.value && tss.value.children[s]) {
446
+ tss.value.scrollTop = tss.value.children[0].offsetTop;
447
+ }
448
+ }
449
+ };
450
+
180
451
  watch(
181
452
  () => props.modelValue,
182
453
  (value: any) => {
183
- state.date = value || '';
184
- state.value = value || '';
454
+ const formatted = value ? idate(value).format(panelFormat.value) : undefined;
455
+ // 面板显示日期
456
+ state.panel = formatted;
457
+ // 值
458
+ state.value = formatted;
185
459
  },
186
460
  { deep: true, immediate: true },
187
461
  );
188
- // 范围时间值
462
+
463
+ onMounted(() => {
464
+ setTimeValue();
465
+ });
189
466
  watch(
190
- () => props.rangeValue,
191
- (value: any) => {
192
- state.rangeValue = value;
467
+ () => state.panel,
468
+ () => {
469
+ setTimeValue();
193
470
  },
194
- { deep: true, immediate: true },
195
471
  );
196
472
 
197
473
  // 更新时间
198
- const upload = (a: any = 'm', b: any = 1) => {
199
- const date = cDate(state.date);
200
- if (a === 'm') {
201
- date.setMonth(date.getMonth() + b);
202
- } else {
203
- date.setFullYear(date.getFullYear() + b);
474
+ const upload = (type: string = 'm', value: number = 0, add: number = 1) => {
475
+ const date = cDate(state.panel);
476
+
477
+ switch (type) {
478
+ case 'y':
479
+ if (value !== 0) {
480
+ date.setFullYear(value);
481
+ } else {
482
+ date.setFullYear(date.getFullYear() + add);
483
+ }
484
+ break;
485
+ case 'm':
486
+ if (value !== 0) {
487
+ date.setMonth(value - 1);
488
+ } else {
489
+ date.setMonth(date.getMonth() + add);
490
+ }
491
+ break;
492
+ case 'd':
493
+ if (value !== 0) {
494
+ date.setDate(value);
495
+ } else {
496
+ date.setDate(date.getDate() + add);
497
+ }
498
+ break;
499
+ case 'h':
500
+ if (value !== 0) {
501
+ date.setHours(value);
502
+ } else {
503
+ date.setHours(date.getHours() + add);
504
+ }
505
+ break;
506
+ case 'n':
507
+ if (value !== 0) {
508
+ date.setMinutes(value);
509
+ } else {
510
+ date.setMinutes(date.getMinutes() + add);
511
+ }
512
+ break;
513
+ case 's':
514
+ if (value !== 0) {
515
+ date.setSeconds(value);
516
+ } else {
517
+ date.setSeconds(date.getSeconds() + add);
518
+ }
519
+ break;
520
+ default:
521
+ break;
204
522
  }
205
- return idate(date).format(props.format);
523
+ return idate(date).format(panelFormat.value);
206
524
  };
207
525
 
208
526
  // 日期前翻
209
- const prev = (type: any = 'm') => {
210
- state.date = upload(type, -1);
527
+ const prev = () => {
528
+ state.panel = upload(state.type > 0 ? 'y' : 'm', 0, state.type === 2 ? -10 : -1);
211
529
  };
212
530
  // 日期后翻
213
- const next = (type: any = 'm') => {
214
- state.date = upload(type, 1);
215
- };
216
- // 设置范围时间
217
- const setRangeValue = (item: any) => {
218
- if (state.value) {
219
- state.rangeValue = idate(item.date).format(props.format);
220
- }
531
+ const next = () => {
532
+ state.panel = upload(state.type > 0 ? 'y' : 'm', 0, state.type === 2 ? 10 : 1);
221
533
  };
222
534
 
223
535
  // 向外通信
224
536
  const change = (item: any) => {
225
- emits('update:modelValue', item ? idate(item.date).format(props.format) : item);
226
- emits('change', state.value);
537
+ const panelValue = item ? idate(item.date).format(panelFormat.value) : item;
538
+ state.panel = panelValue;
539
+ state.value = panelValue;
540
+ emits('change', panelValue);
541
+ if (state.isNoTime) {
542
+ update();
543
+ }
544
+ };
545
+
546
+ // 清空日期
547
+ const remove = () => {
548
+ state.panel = undefined;
549
+ state.value = undefined;
550
+ update();
551
+ };
552
+
553
+ // 更新数据
554
+ const update = () => {
555
+ const nextValue = state.panel ? idate(state.panel).format(panelFormat.value) : undefined;
556
+ if (state.panel && nextValue !== state.panel) {
557
+ state.panel = nextValue;
558
+ }
559
+ state.value = nextValue;
560
+ emits('update:modelValue', nextValue);
561
+ emits('update', nextValue);
562
+ };
563
+
564
+ // 面板类型
565
+ const setPanelType = () => {
566
+ if (state.type < 2) {
567
+ state.type += 1;
568
+ }
569
+ };
570
+
571
+ // 时间滚动
572
+ const _timer: any = ref(null);
573
+ const timeScroll = (ev: any) => {
574
+ const item: any = ev.target?.children[0];
575
+ const type: any = ev.target.getAttribute('data-type') || '';
576
+ if (state.panel && item) {
577
+ clearTimeout(_timer.value);
578
+ _timer.value = setTimeout(() => {
579
+ const h = item.offsetHeight;
580
+ const idx = Math.floor(ev.target.scrollTop / h);
581
+ const maxIdx = type === 'h' ? 23 : 59;
582
+ const safeIdx = Math.min(Math.max(idx, 0), maxIdx);
583
+ const { YYYY = 0, MM = 0, DD = 0, HH = '', mm = '', ss = '' }: any = state.curr || {};
584
+ let _panel = `${YYYY}-${MM}-${DD}`;
585
+ if (type === 'h') {
586
+ _panel += ` ${safeIdx}:${mm}:${ss}`;
587
+ }
588
+ if (type === 'm') {
589
+ _panel += ` ${HH}:${safeIdx}:${ss}`;
590
+ }
591
+ if (type === 's') {
592
+ _panel += ` ${HH}:${mm}:${safeIdx}`;
593
+ }
594
+ state.panel = idate(_panel).format('YYYY-MM-DD HH:mm:ss');
595
+ }, 100);
596
+ }
227
597
  };
228
- // 向外通信范围时间
229
598
  </script>
599
+ <style lang="scss">
600
+ .fekit-date-view-time-scroll {
601
+ position: relative;
602
+ height: 100%;
603
+ scroll-snap-type: y mandatory;
604
+ & > * {
605
+ scroll-snap-align: center;
606
+ }
607
+ }
608
+ </style>