@neatui/nuxt 1.0.6 → 1.0.8

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": "@neatui/nuxt",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "NeatUI component library for Nuxt 3",
5
5
  "main": "./src/index.ts",
6
6
  "license": "MIT",
@@ -37,6 +37,7 @@
37
37
  "@fekit/scrollto": "^3.0.3",
38
38
  "@fekit/toast": "^2.7.2",
39
39
  "@fekit/utils": "^4.3.1",
40
+ "@neatui/css": "^3.10.3",
40
41
  "@rollup/plugin-commonjs": "^26.0.1",
41
42
  "@rollup/plugin-node-resolve": "^15.2.3",
42
43
  "@vue/compiler-sfc": "^3.4.30",
@@ -3,24 +3,33 @@
3
3
  <slot>
4
4
  <button ui-btn="@a s case :border"><i class="icon icon-dropdown"></i></button>
5
5
  </slot>
6
- <Teleport to="#ifollow" v-if="$slots.tips">
7
- <div
8
- ref="tip"
9
- :class="tipClass"
10
- :style="`position: fixed; pointer-events: none; ${tipStyle}`"
11
- ui-tips="@a"
12
- :ui-tips-view="state.show ? 1 : 0"
13
- @mouseover="hovershow"
14
- @mouseout="hoverhide"
15
- @click="clickhide"
16
- >
17
- <div ref="box" :ui-tips-box="state.pos" :class="tipBoxClass" :style="tipBoxStyle" @mousedown="downdom">
18
- <slot name="tips"></slot>
19
- <div v-if="arrow" ui-tips-arrow="" v-bind="arrow"></div>
6
+ </div>
7
+ <Teleport v-if="$slots.tips" to="#ifollow">
8
+ <div
9
+ ref="tip"
10
+ :class="`${tipClass}${mobile ? ' fekit-follow-mobile' : ''}`"
11
+ :style="`position: fixed; pointer-events: none; ${tipStyle}`"
12
+ ui-tips="@a"
13
+ :ui-tips-view="state.show ? 1 : 0"
14
+ @mouseover="hovershow"
15
+ @mouseout="hoverhide"
16
+ @click="clickhide"
17
+ >
18
+ <div ref="box" :ui-tips-box="state.pos" :class="tipBoxClass" :style="tipBoxStyle">
19
+ <div ui-hide=">mob" class="mob-follow-tips w-full n-ss" ui-flex="row xm">
20
+ <div class="w-sm"></div>
21
+ <div>
22
+ <h5 class="bold">{{ title }}</h5>
23
+ </div>
24
+ <div class="w-sm" ui-flex="row rm">
25
+ <button ui-btn="@a none s :square" @click="toggle"><i class="icon icon-close"></i></button>
26
+ </div>
20
27
  </div>
28
+ <slot name="tips"></slot>
29
+ <div v-if="arrow" ui-tips-arrow="" v-bind="arrow"></div>
21
30
  </div>
22
- </Teleport>
23
- </div>
31
+ </div>
32
+ </Teleport>
24
33
  </template>
25
34
  <script setup lang="ts">
26
35
  import { reactive, ref, onMounted, onUnmounted, watch } from 'vue';
@@ -28,11 +37,13 @@
28
37
 
29
38
  const emits = defineEmits(['update:modelValue', 'onhide', 'onshow']);
30
39
 
31
- const area: any = document.querySelector('#ifollow');
32
- if (!area) {
33
- const el = document.createElement('div');
34
- el.id = 'ifollow';
35
- document.body.appendChild(el);
40
+ if (import.meta.client) {
41
+ const area = document.querySelector('#ifollow');
42
+ if (!area) {
43
+ const el = document.createElement('div');
44
+ el.id = 'ifollow';
45
+ document.body.appendChild(el);
46
+ }
36
47
  }
37
48
 
38
49
  // 类型
@@ -48,6 +59,8 @@
48
59
  tipBoxStyle?: string;
49
60
  arrow?: any;
50
61
  clickTipHide?: boolean;
62
+ mobile?: boolean;
63
+ title?: string;
51
64
  }
52
65
 
53
66
  // 入参
@@ -63,6 +76,8 @@
63
76
  tipBoxStyle: '',
64
77
  arrow: {},
65
78
  clickTipHide: false,
79
+ mobile: true,
80
+ title: '',
66
81
  });
67
82
 
68
83
  const dom: any = ref(null);
@@ -74,6 +89,7 @@
74
89
  pos: props.pos,
75
90
  hover: 0,
76
91
  tipdom: false,
92
+ isMobile: false,
77
93
  });
78
94
 
79
95
  const showTimer: any = ref(null);
@@ -148,42 +164,47 @@
148
164
  }
149
165
  };
150
166
 
151
- const downdom = (ev: any) => {
152
- // 不记得了这个当时为什么要加上这个,这个会影响单选框的选中后不自动关闭下拉框啊~
153
- // state.tipdom = true;
154
- // document.addEventListener(
155
- // 'mouseup',
156
- // () => {
157
- // setTimeout(() => {
158
- // state.tipdom = false;
159
- // }, 10);
160
- // },
161
- // true
162
- // );
167
+ // 阻止触摸事件冒泡和默认行为
168
+ const preventTouchScroll = (e: TouchEvent) => {
169
+ // 只有当触摸点在遮罩层本身时才阻止,不影响子元素
170
+ if (e.target === tip.value && tip.value?.classList.contains('fekit-follow-mobile')) {
171
+ e.preventDefault();
172
+ e.stopPropagation();
173
+ }
163
174
  };
164
175
 
165
176
  onMounted(() => {
166
177
  document.addEventListener('click', cancel, true);
178
+ // 添加触摸事件监听器以防止页面滚动
179
+ document.addEventListener('touchstart', preventTouchScroll, { passive: false });
180
+ document.addEventListener('touchmove', preventTouchScroll, { passive: false });
181
+ // 判断是否为移动端
182
+ state.isMobile = window.innerWidth < 800;
167
183
  });
168
184
 
169
185
  const ex: any = ref(null);
170
186
  watch([() => dom.value, () => tip.value], () => {
171
187
  if (dom.value && tip.value) {
172
- ex.value = new Follow(dom.value, tip.value, (status) => {
173
- if (!props?.pos?.includes(':fixed')) {
174
- if (status === 'b') {
175
- const _pos = props?.pos?.replace(/b/g, 't');
176
- if (_pos) {
177
- state.pos = _pos;
178
- }
179
- } else {
180
- const _pos = props?.pos?.replace(/t/g, 'b');
181
- if (_pos) {
182
- state.pos = _pos;
188
+ ex.value = new Follow(
189
+ dom.value,
190
+ tip.value,
191
+ (status) => {
192
+ if (!props?.pos?.includes(':fixed')) {
193
+ if (status === 'b') {
194
+ const _pos = props?.pos?.replace(/b/g, 't');
195
+ if (_pos) {
196
+ state.pos = _pos;
197
+ }
198
+ } else {
199
+ const _pos = props?.pos?.replace(/t/g, 'b');
200
+ if (_pos) {
201
+ state.pos = _pos;
202
+ }
183
203
  }
184
204
  }
185
- }
186
- });
205
+ },
206
+ { hideOnScrollOut: false },
207
+ );
187
208
  } else {
188
209
  ex.value = null;
189
210
  }
@@ -191,6 +212,9 @@
191
212
 
192
213
  onUnmounted(() => {
193
214
  document.removeEventListener('click', cancel, true);
215
+ // 移除触摸事件监听器
216
+ document.removeEventListener('touchstart', preventTouchScroll);
217
+ document.removeEventListener('touchmove', preventTouchScroll);
194
218
  state.show = 0;
195
219
 
196
220
  if (ex.value) {
@@ -218,3 +242,64 @@
218
242
  cancel,
219
243
  });
220
244
  </script>
245
+ <style lang="scss">
246
+ @use '@neatui/css' as neatui;
247
+ @include neatui.mob {
248
+ .fekit-follow-mobile {
249
+ position: fixed !important;
250
+ width: 100% !important;
251
+ height: 100% !important;
252
+
253
+ display: flex !important;
254
+ flex-direction: column !important;
255
+ justify-content: flex-end !important;
256
+ align-items: center !important;
257
+ align-content: center !important;
258
+ inset: 0 0 0 0 !important;
259
+
260
+ &[ui-tips-view='1'] {
261
+ background-color: rgba(0, 0, 0, 0.5);
262
+ pointer-events: auto !important;
263
+ touch-action: none !important;
264
+ }
265
+
266
+ &[ui-tips~='@a'] [ui-tips-box] {
267
+ position: relative !important;
268
+
269
+ width: 100% !important;
270
+ bottom: env(safe-area-inset-bottom) !important;
271
+ right: unset !important;
272
+ top: unset !important;
273
+ left: unset !important;
274
+ transform: translate3d(0, 0.5em, 0);
275
+
276
+ display: flex !important;
277
+ flex-direction: column !important;
278
+ justify-content: flex-end !important;
279
+ align-items: center !important;
280
+ align-content: center !important;
281
+
282
+ [ui-tips-arrow] {
283
+ display: none;
284
+ }
285
+ }
286
+ }
287
+
288
+ .mob-follow-tips {
289
+ position: relative;
290
+ padding-bottom: env(safe-area-inset-bottom, 16px);
291
+ &::before {
292
+ position: absolute;
293
+ content: '';
294
+ display: block;
295
+ width: 4rem;
296
+ height: 3px;
297
+ border-radius: 1em;
298
+ background-color: var(--co-text-ss);
299
+ top: 5px;
300
+ left: 50%;
301
+ transform: translateX(-50%);
302
+ }
303
+ }
304
+ }
305
+ </style>
@@ -3,7 +3,7 @@
3
3
  <div :class="listClass" :style="listStyle" am-view-item="list">
4
4
  <slot name="list" :mode="mode"></slot>
5
5
  </div>
6
- <div :class="itemClass" :style="itemStyle" am-view-item="item" ref="itemRef" v-if="view === 'iteming' || view === 'listing' || view === 'item'">
6
+ <div v-if="view === 'iteming' || view === 'listing' || view === 'item'" ref="itemRef" :class="itemClass" :style="itemStyle" am-view-item="item">
7
7
  <div class="iroute-view-dark" @click="cancel"></div>
8
8
  <div class="iroute-view-body" :style="mode === 'preview' || mode === 'pop-up' ? '' : 'width: 100%;'">
9
9
  <slot name="item" :mode="mode"></slot>
@@ -64,17 +64,17 @@
64
64
  );
65
65
  };
66
66
  };
67
- provide('onlist', evs('list', 'item'));
68
- provide('onitem', evs('item', 'list'));
69
67
 
70
68
  const init = ref(0);
71
69
  const view = ref('');
72
70
  const anim = (e: any) => {
73
71
  if (
74
72
  e.animationName === 'am-show-default' ||
73
+ e.animationName === 'am-show-window' ||
75
74
  e.animationName === 'am-show-preview' ||
76
75
  e.animationName === 'am-show-pop-up' ||
77
76
  e.animationName === 'am-hide-default' ||
77
+ e.animationName === 'am-hide-window' ||
78
78
  e.animationName === 'am-hide-preview' ||
79
79
  e.animationName === 'am-hide-pop-up'
80
80
  ) {
@@ -93,12 +93,17 @@
93
93
  }
94
94
  };
95
95
 
96
- watch(() => route.query[props.queryName], a);
97
- a();
96
+ if (import.meta.client) {
97
+ provide('onlist', evs('list', 'item'));
98
+ provide('onitem', evs('item', 'list'));
99
+ watch(() => route.query[props.queryName], a);
100
+ a();
101
+ }
98
102
 
99
103
  // 取消
100
- const cancel = ({ target }: any = {}) => {
104
+ const cancel = () => {
101
105
  const _query = { ...route.query };
106
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
102
107
  delete _query[props.queryName];
103
108
  router.push({ path: route.path, query: _query });
104
109
  };
@@ -172,27 +177,27 @@
172
177
  }
173
178
  }
174
179
  &[am-view='list'] {
175
- & > [am-view-item~='list'] {
180
+ [am-view-item~='list'] {
176
181
  flex-direction: column;
177
182
  display: flex;
178
183
  animation: am-show-default 0.35s ease both;
179
184
  }
180
185
  }
181
186
  &[am-view='listing'] {
182
- & > [am-view-item~='item'] {
187
+ [am-view-item~='item'] {
183
188
  flex-direction: column;
184
189
  display: flex;
185
190
  animation: am-hide-default 0.3s ease-in-out both;
186
191
  }
187
192
  }
188
193
  &[am-view='item'] {
189
- & > [am-view-item~='item'] {
194
+ [am-view-item~='item'] {
190
195
  display: flex;
191
196
  animation: am-show-default 0.35s ease both;
192
197
  }
193
198
  }
194
199
  &[am-view='iteming'] {
195
- & > [am-view-item~='list'] {
200
+ [am-view-item~='list'] {
196
201
  display: flex;
197
202
  animation: am-hide-default 0.3s ease-in-out both;
198
203
  }
@@ -259,14 +264,14 @@
259
264
  }
260
265
  &[am-view='list'],
261
266
  &[am-view='listing'] {
262
- & > [am-view-item~='item'] {
267
+ [am-view-item~='item'] {
263
268
  display: flex;
264
269
  animation: am-hide-preview 0.5s ease-in-out both;
265
270
  }
266
271
  }
267
272
  &[am-view='item'],
268
273
  &[am-view='iteming'] {
269
- & > [am-view-item~='item'] {
274
+ [am-view-item~='item'] {
270
275
  display: flex;
271
276
  animation: am-show-preview 0.5s ease both;
272
277
  }
@@ -353,7 +358,7 @@
353
358
  }
354
359
  &[am-view='list'],
355
360
  &[am-view='listing'] {
356
- & > [am-view-item~='item'] {
361
+ [am-view-item~='item'] {
357
362
  display: flex;
358
363
  opacity: 0;
359
364
  & > * {
@@ -363,7 +368,7 @@
363
368
  }
364
369
  &[am-view='item'],
365
370
  &[am-view='iteming'] {
366
- & > [am-view-item~='item'] {
371
+ [am-view-item~='item'] {
367
372
  display: flex;
368
373
  opacity: 1;
369
374
  & > * {
@@ -372,4 +377,73 @@
372
377
  }
373
378
  }
374
379
  }
380
+
381
+ // 窗口模式
382
+ [am-view][am-mode~='window'] {
383
+ overflow: hidden;
384
+
385
+ @keyframes am-show-default {
386
+ 0% {
387
+ opacity: 0;
388
+ transform: translate3d(1em, 0, 0);
389
+ }
390
+ 100% {
391
+ opacity: 1;
392
+ transform: translate3d(0, 0, 0);
393
+ }
394
+ }
395
+ @keyframes am-hide-default {
396
+ 0% {
397
+ opacity: 1;
398
+ transform: translate3d(0, 0, 0);
399
+ }
400
+ 100% {
401
+ opacity: 0;
402
+ transform: translate3d(1em, 0, 0);
403
+ }
404
+ }
405
+
406
+ &[view-full~='1'] {
407
+ position: fixed;
408
+ width: 100%;
409
+ height: 100%;
410
+ left: 0;
411
+ top: 0;
412
+ z-index: 15;
413
+ }
414
+
415
+ [am-view-item] {
416
+ display: none;
417
+
418
+ &[am-view-item~='row'] {
419
+ flex-direction: row !important;
420
+ }
421
+ }
422
+ &[am-view='list'] {
423
+ [am-view-item~='list'] {
424
+ flex-direction: column;
425
+ display: flex;
426
+ animation: am-show-default 0s ease both;
427
+ }
428
+ }
429
+ &[am-view='listing'] {
430
+ [am-view-item~='item'] {
431
+ flex-direction: column;
432
+ display: flex;
433
+ animation: am-hide-default 0s ease-in-out both;
434
+ }
435
+ }
436
+ &[am-view='item'] {
437
+ [am-view-item~='item'] {
438
+ display: flex;
439
+ animation: am-show-default 0s ease both;
440
+ }
441
+ }
442
+ &[am-view='iteming'] {
443
+ [am-view-item~='list'] {
444
+ display: flex;
445
+ animation: am-hide-default 0s ease-in-out both;
446
+ }
447
+ }
448
+ }
375
449
  </style>
@@ -1,63 +1,46 @@
1
1
  <!-- 实验性插件 -->
2
2
  <template>
3
- <Teleport :to="area">
3
+ <Teleport to="area">
4
4
  <div
5
5
  v-if="state.view"
6
6
  :am-view="am"
7
7
  :data-key="id"
8
8
  :style="`${`${dark}` ? 'background-color: rgba(0, 0, 0, ' + dark + ');' : ''}${zIndex ? 'z-index:' + zIndex + ';' : ''}`"
9
9
  :view="state.view"
10
- class="fekit-layer"
11
- >
10
+ class="fekit-layer">
12
11
  <div :ui-flex="`col ${flex}`" :style="wrapStyle" class="fekit-layer-wrap" @animationend="anim" @click="_sys_hide">
13
- <slot></slot>
12
+ <slot :param="_tasks[id].params"></slot>
14
13
  </div>
15
14
  </div>
16
15
  </Teleport>
17
16
  </template>
18
17
  <script lang="ts" setup>
19
- import { onBeforeUnmount, PropType, reactive, ref, watch } from 'vue';
18
+ import { onBeforeUnmount, reactive, watch } from 'vue';
20
19
  import { isFunction } from '@fekit/utils';
20
+ import { _tasks } from './index';
21
21
 
22
- // 弹层队列
23
- if (!(window as any)._FEKIT_LAYER_TASKS) {
24
- (window as any)._FEKIT_LAYER_TASKS = {};
22
+ // 类型定义
23
+ interface Props {
24
+ zIndex?: string | number;
25
+ area?: string;
26
+ id: string;
27
+ am?: 'aa' | 'ab' | 'ac' | 'ad' | 'ae' | 'af' | 'ag' | 'ah' | 'ai' | 'aj' | 'ak' | 'al' | 'am' | 'an' | 'ao' | 'ap' | 'aq' | 'ar' | 'as' | 'at';
28
+ flex?: 'lt' | 'ct' | 'rt' | 'lm' | 'cm' | 'rm' | 'lb' | 'cb' | 'rb';
29
+ dark?: string | number;
30
+ darkClickClose?: boolean;
31
+ wrapStyle?: string;
25
32
  }
26
- const _tasks: any = (window as any)._FEKIT_LAYER_TASKS;
27
33
 
28
34
  // 弹窗入参
29
- const props: any = defineProps({
30
- zIndex: {
31
- type: [String, Number],
32
- default: '',
33
- },
34
- area: {
35
- type: String,
36
- default: '#fekit',
37
- },
38
- id: {
39
- type: String,
40
- },
41
- am: {
42
- type: String as PropType<'aa' | 'ab' | 'ac' | 'ad' | 'ae' | 'af' | 'ag' | 'ah' | 'ai' | 'aj' | 'ak' | 'al' | 'am' | 'an' | 'ao' | 'ap' | 'aq' | 'ar' | 'as' | 'at'>,
43
- default: 'as',
44
- },
45
- flex: {
46
- type: String as PropType<'lt' | 'ct' | 'rt' | 'lm' | 'cm' | 'rm' | 'lb' | 'cb' | 'rb'>,
47
- default: 'cm',
48
- },
49
- dark: {
50
- type: [String, Number],
51
- default: '',
52
- },
53
- darkClickClose: {
54
- type: Boolean,
55
- default: true,
56
- },
57
- wrapStyle: {
58
- type: String,
59
- default: '',
60
- },
35
+ const props = withDefaults(defineProps<Props>(), {
36
+ zIndex: '',
37
+ area: '#fekit',
38
+ id: '',
39
+ am: 'as',
40
+ flex: 'cm',
41
+ dark: '',
42
+ darkClickClose: true,
43
+ wrapStyle: '',
61
44
  });
62
45
 
63
46
  const _area: any = document.querySelector(props.area);
@@ -77,10 +60,12 @@
77
60
 
78
61
  function disableScroll() {
79
62
  state.top = document.documentElement.scrollTop;
63
+ const sw = window.innerWidth - document.documentElement.clientWidth;
80
64
  document.body.style.overflow = 'hidden';
81
65
  document.body.style.position = 'fixed';
82
66
  document.body.style.top = `-${state.top}px`;
83
67
  document.body.style.width = '100%';
68
+ document.body.style.paddingRight = `${sw}px`;
84
69
  }
85
70
 
86
71
  function enableScroll() {
@@ -88,6 +73,7 @@
88
73
  document.body.style.removeProperty('position');
89
74
  document.body.style.removeProperty('top');
90
75
  document.body.style.removeProperty('width');
76
+ document.body.style.removeProperty('padding-right');
91
77
  window.scrollTo(0, state.top);
92
78
  }
93
79
 
@@ -185,21 +171,22 @@
185
171
  // 注册弹层
186
172
  const { id = '' }: any = { ...props };
187
173
  if (_tasks[id]) {
188
- console.error('Layer 有重复 id', id);
189
174
  }
190
175
  if (id) {
191
176
  _tasks[id] = { init, show, hide, open, none };
192
177
  } else {
193
- console.error('Layer 必须填写全局统一的 id');
194
178
  }
195
179
 
196
180
  // 卸载弹层
197
181
  onBeforeUnmount(() => {
182
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
198
183
  delete _tasks[id];
199
184
  });
200
185
  </script>
201
186
 
202
187
  <style lang="scss">
188
+ @use '@neatui/css' as neatui;
189
+
203
190
  // 弹层
204
191
  .fekit-layer {
205
192
  position: fixed;
@@ -232,21 +219,20 @@
232
219
  width: 2em;
233
220
  height: 2em;
234
221
 
235
- color: var(--co-read);
222
+ color: var(--co-vice);
236
223
  bottom: auto;
237
- top: 0.5em;
238
- right: 0.5em;
224
+ top: 1.5em;
225
+ right: 1.5em;
239
226
  left: auto;
240
227
  box-shadow: none;
241
228
 
242
229
  padding: 0.25em;
243
230
  margin-left: -1rem;
244
231
  text-align: center;
245
- transform: scale(0.8);
232
+ transform: scale(0.9);
246
233
  transition: all 0.2s;
247
234
  border-radius: 50%;
248
- background:
249
- linear-gradient(45deg, transparent 44%, currentColor 45%, currentColor 55%, transparent 56%) no-repeat center / 50% 50%,
235
+ background: linear-gradient(45deg, transparent 44%, currentColor 45%, currentColor 55%, transparent 56%) no-repeat center / 50% 50%,
250
236
  linear-gradient(-45deg, transparent 44%, currentColor 45%, currentColor 55%, transparent 56%) no-repeat center / 50% 50%;
251
237
 
252
238
  &:active {
@@ -256,11 +242,25 @@
256
242
 
257
243
  .fekit-layer-close {
258
244
  &:hover {
259
- transform: scale(0.8) rotate(90deg);
245
+ transform: scale(0.9) rotate(90deg);
260
246
  }
261
247
  &:active {
262
248
  transform: scale(0.7) rotate(90deg);
263
249
  }
264
250
  }
265
251
  }
252
+
253
+ @include neatui.mob {
254
+ .fekit-layer-close {
255
+ top: 1.5em;
256
+ right: 1.5em;
257
+
258
+ color: var(--co-fore);
259
+ top: auto;
260
+ right: auto;
261
+ left: 50%;
262
+ bottom: -2.5rem;
263
+ box-shadow: 0 0 0 2px currentColor;
264
+ }
265
+ }
266
266
  </style>
@@ -1,5 +1,5 @@
1
- import Layer from './Layer.vue';
1
+ export const _tasks: any = {};
2
2
  const LayerById = (id: any) => {
3
- return (window as any)._FEKIT_LAYER_TASKS[id];
3
+ return _tasks[id];
4
4
  };
5
- export { Layer, LayerById };
5
+ export default LayerById;
@@ -1,4 +1,5 @@
1
- import { Layer, LayerById } from './LayerView';
1
+ import Layer from './LayerView/Layer.vue';
2
+ import LayerById from './LayerView/index';
2
3
  import Icon from './Icon.vue';
3
4
  import IFollowView from './IFollowView.vue';
4
5
  import IPickerView from './IPickerView.vue';
@@ -66,7 +66,7 @@
66
66
  </div>
67
67
  </div>
68
68
  <div v-if="!state.isNoTime" class="ny-sm nx-ms">
69
- <div class="full ac b-solid bk-back b-xs r-sm" ui-row="mob-8" style="width: 100%; height: 3em; padding: 0.5em">
69
+ <div class="full ac b-solid bk-line b-xs r-sm" ui-row="mob-8" style="width: 100%; height: 3em; padding: 0.5em">
70
70
  <ul
71
71
  :style="`${state.panel ? '' : 'pointer-events: none; opacity:.35'}`"
72
72
  class="fekit-date-view-time-scroll"
@@ -1,13 +1,13 @@
1
1
  <template>
2
2
  <div ui-date="@a" class="fekit-date-view pr" :class="`${frame ? 'b-solid bk-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 bk-back" ui-flex="row xm">
3
+ <div ui-date-head="" class="flex-fixed nx-ms mt-ss ny-sm b-solid bk-line" ui-flex="row xm">
4
4
  <div ui-flex="row lm" class="o-lm">
5
5
  <div ui-btn="@a s none :square" @click="prev()">
6
6
  <Icon name="arrow-left" />
7
7
  </div>
8
8
  </div>
9
9
  <div ui-btns="@a none s" @click="setPanelType()">
10
- <p v-if="state.type > 2" class="nowrap" ui-flex="row cm">
10
+ <p v-if="state.type === 2" class="nowrap" ui-flex="row cm">
11
11
  <b class="nx-ss">{{ panel.sta }}年</b>
12
12
  <span class="nx-xs">-</span>
13
13
  <b class="nx-ss">{{ panel.end }}年</b>
@@ -26,32 +26,38 @@
26
26
  </div>
27
27
  </div>
28
28
  </div>
29
- <div ui-date-body="" class="flex-block pr">
30
- <div v-if="state.type === 2" class="full n-ms">
31
- <ul ui-row="mob-6" class="n-ms ny-sm-sub">
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
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-xs+ h-xs+ ac ux-none co-main:hover">{{ year }}</div>
33
+ <div
34
+ ui-flex="col cm"
35
+ class="w-ss h-xs ac ux-none r-sm"
36
+ :class="isSelectedYear(year) ? 'active bg-main-o-lm co-w' : 'co-main:hover'"
37
+ >
38
+ {{ year }}
39
+ </div>
34
40
  </li>
35
41
  </ul>
36
42
  </div>
37
43
  <div v-else-if="state.type === 1" class="full">
38
- <ul ui-row="mob-4" class="n-ms ny-sm-sub">
39
- <li ui-flex="col cm" @click="setDateValue('m', 1)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">1月</div></li>
40
- <li ui-flex="col cm" @click="setDateValue('m', 2)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">2月</div></li>
41
- <li ui-flex="col cm" @click="setDateValue('m', 3)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">3月</div></li>
42
- <li ui-flex="col cm" @click="setDateValue('m', 4)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">4月</div></li>
43
- <li ui-flex="col cm" @click="setDateValue('m', 5)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">5月</div></li>
44
- <li ui-flex="col cm" @click="setDateValue('m', 6)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">6月</div></li>
45
- <li ui-flex="col cm" @click="setDateValue('m', 7)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">7月</div></li>
46
- <li ui-flex="col cm" @click="setDateValue('m', 8)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">8月</div></li>
47
- <li ui-flex="col cm" @click="setDateValue('m', 9)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">9月</div></li>
48
- <li ui-flex="col cm" @click="setDateValue('m', 10)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">10月</div></li>
49
- <li ui-flex="col cm" @click="setDateValue('m', 11)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">11月</div></li>
50
- <li ui-flex="col cm" @click="setDateValue('m', 12)"><div ui-flex="col cm" class="w-xs+ h-xs+ ac ux-none co-main:hover">12月</div></li>
44
+ <ul ui-grid="mob:4" class="n-ms ny-sm-sub">
45
+ <li ui-flex="col cm" @click="setDateValue('m', 1)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(1) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">1月</div></li>
46
+ <li ui-flex="col cm" @click="setDateValue('m', 2)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(2) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">2月</div></li>
47
+ <li ui-flex="col cm" @click="setDateValue('m', 3)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(3) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">3月</div></li>
48
+ <li ui-flex="col cm" @click="setDateValue('m', 4)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(4) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">4月</div></li>
49
+ <li ui-flex="col cm" @click="setDateValue('m', 5)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(5) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">5月</div></li>
50
+ <li ui-flex="col cm" @click="setDateValue('m', 6)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(6) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">6月</div></li>
51
+ <li ui-flex="col cm" @click="setDateValue('m', 7)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(7) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">7月</div></li>
52
+ <li ui-flex="col cm" @click="setDateValue('m', 8)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(8) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">8月</div></li>
53
+ <li ui-flex="col cm" @click="setDateValue('m', 9)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(9) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">9月</div></li>
54
+ <li ui-flex="col cm" @click="setDateValue('m', 10)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(10) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">10月</div></li>
55
+ <li ui-flex="col cm" @click="setDateValue('m', 11)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(11) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">11月</div></li>
56
+ <li ui-flex="col cm" @click="setDateValue('m', 12)"><div ui-flex="col cm" class="w-ss h-xs ac ux-none r-sm" :class="isSelectedMonth(12) ? 'active bg-main-o-lm co-w' : 'co-main:hover'">12月</div></li>
51
57
  </ul>
52
58
  </div>
53
59
  <div v-else class="full">
54
- <ul ui-flex="row am" class="fs-xs nx-sm nt-sm">
60
+ <ul ui-flex="row am" class="fs-xs nx-sm nt-ss">
55
61
  <li class="flex-block ac"><span>日</span></li>
56
62
  <li class="flex-block ac"><span>一</span></li>
57
63
  <li class="flex-block ac"><span>二</span></li>
@@ -73,9 +79,9 @@
73
79
  >
74
80
  <div
75
81
  ui-flex="col cm"
76
- class="r-sm b-solid bk-none b-xs my-ss ux-scale co-main:hover"
82
+ class="r-sm b-solid bk-none b-xs my-ss ux-scale"
77
83
  style="width: 1.8em; height: 1.8em"
78
- :class="`${item.isLastMonth || item.isNextMonth ? 'co-idle' : item.isHoliday ? 'co-risk' : ''} ${item.selected ? 'active bg-main-o-lm co-fore' : 'hover-bg-weak'}`"
84
+ :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'}`"
79
85
  >
80
86
  <span>{{ item.day }}</span>
81
87
  </div>
@@ -86,54 +92,57 @@
86
92
  </ul>
87
93
  </div>
88
94
  </div>
89
- <div v-if="!state.isNoTime && state.type === 0" class="ny-sm nx-ms">
90
- <div class="full ac b-solid bk-back b-xs r-sm" ui-row="mob-8" style="width: 100%; height: 3em; padding: 0.5em">
91
- <ul
92
- :style="`${state.panel ? '' : 'pointer-events: none; opacity:.35'}`"
93
- class="fekit-date-view-time-scroll"
94
- ref="ths"
95
- ui-scroll=":y xs"
96
- ui-btns="@a none s :block"
97
- data-type="h"
98
- @scroll="timeScroll"
99
- >
100
- <li v-for="(item, idx) in hours" :key="idx">
101
- {{ item }}
102
- </li>
103
- </ul>
104
- <ul
105
- :style="`${state.panel ? '' : 'pointer-events: none; opacity:.35'}`"
106
- class="fekit-date-view-time-scroll"
107
- ref="tms"
108
- ui-scroll=":y xs"
109
- ui-btns="@a none s :block"
110
- data-type="m"
111
- @scroll="timeScroll"
112
- >
113
- <li v-for="(item, idx) in seconds" :key="idx">
114
- {{ item }}
115
- </li>
116
- </ul>
117
- <ul
118
- :style="`${state.panel ? '' : 'pointer-events: none; opacity:.35'}`"
119
- class="fekit-date-view-time-scroll"
120
- ref="tss"
121
- ui-scroll=":y xs"
122
- ui-btns="@a none s :block"
123
- data-type="s"
124
- @scroll="timeScroll"
125
- >
126
- <li v-for="(item, idx) in seconds" :key="idx">
127
- {{ item }}
128
- </li>
129
- </ul>
95
+ <template v-if="!state.isNoTime">
96
+ <div v-if="state.type === 0" class="h-ss ny-sm nx-ms">
97
+ <div class="full ac b-solid bk-line b-xs r-sm" ui-row="mob-8" style="width: 100%; height: 3em; padding: 0.5em">
98
+ <ul
99
+ :style="`${state.panel ? '' : 'pointer-events: none; opacity:.35'}`"
100
+ class="fekit-date-view-time-scroll"
101
+ ref="ths"
102
+ ui-scroll=":y xs"
103
+ ui-btns="@a none s :block"
104
+ data-type="h"
105
+ @scroll="timeScroll"
106
+ >
107
+ <li v-for="(item, idx) in hours" :key="idx">
108
+ {{ item }}
109
+ </li>
110
+ </ul>
111
+ <ul
112
+ :style="`${state.panel ? '' : 'pointer-events: none; opacity:.35'}`"
113
+ class="fekit-date-view-time-scroll"
114
+ ref="tms"
115
+ ui-scroll=":y xs"
116
+ ui-btns="@a none s :block"
117
+ data-type="m"
118
+ @scroll="timeScroll"
119
+ >
120
+ <li v-for="(item, idx) in seconds" :key="idx">
121
+ {{ item }}
122
+ </li>
123
+ </ul>
124
+ <ul
125
+ :style="`${state.panel ? '' : 'pointer-events: none; opacity:.35'}`"
126
+ class="fekit-date-view-time-scroll"
127
+ ref="tss"
128
+ ui-scroll=":y xs"
129
+ ui-btns="@a none s :block"
130
+ data-type="s"
131
+ @scroll="timeScroll"
132
+ >
133
+ <li v-for="(item, idx) in seconds" :key="idx">
134
+ {{ item }}
135
+ </li>
136
+ </ul>
137
+ </div>
130
138
  </div>
131
- </div>
132
- <div v-if="tools" ui-date-foot="" ui-flex="row xm" class="flex-fixed nx-sl ny-sl b-solid bk-line-o-mm bt-xs">
139
+ <div v-else class="h-ss"></div>
140
+ </template>
141
+ <div v-if="showTools" ui-date-foot="" ui-flex="row xm" class="flex-fixed nx-sl ny-sl b-solid bk-line bt-xs">
133
142
  <div ui-flex="row lm" class="nl-ss">
134
143
  <div v-if="state.panel" ui-flex="row lm">
135
- <span :anim-editing="state.type === 'date'" class="nx-ss">{{ idate(state.panel).format('YYYY-MM-DD') }}</span>
136
- <span v-if="!state.isNoTime" :anim-editing="state.type === 'time'" class="nx-ss">{{ idate(state.panel).format('HH:mm:ss') }}</span>
144
+ <span :anim-editing="state.type === baseType" class="nx-ss">{{ idate(state.panel).format(panelFormat) }}</span>
145
+ <!-- <span v-if="!state.isNoTime" :anim-editing="state.type === baseType" class="nx-ss">{{ idate(state.panel).format('HH:mm:ss') }}</span> -->
137
146
  </div>
138
147
  <div v-else-if="state.shortcuts?.length" ui-flex="row lm">
139
148
  <div class="mr-sm-sub nx-sl-sub ny-ss-sub lh-ss nowrap fs-ss dib-sub" ui-scroll=":x y:hidden">
@@ -153,15 +162,43 @@
153
162
  <script setup lang="ts">
154
163
  import { computed, ref, reactive, watch, onMounted } from 'vue';
155
164
  import { isDateString, isObject, isString } from '@fekit/utils';
156
- // import { idate } from '@fekit/utils';
157
165
  import { idate } from './date';
158
166
  import { Icon } from '../basic';
159
167
 
160
- // const _level: any = { 0: ['d', 1], 1: ['m', 1], 2: ['y', 10], 3: ['y', 100] };
168
+ const normalizeDateInput = (value: any) => {
169
+ if (!value) {
170
+ return value;
171
+ }
172
+ if (isString(value)) {
173
+ if (/^\d{4}$/.test(value)) {
174
+ return `${value}-01-01`;
175
+ }
176
+ if (/^\d{4}-(\d{1,2})$/.test(value)) {
177
+ const [year, month] = value.split('-');
178
+ const m = month.padStart(2, '0');
179
+ return `${year}-${m}-01`;
180
+ }
181
+ }
182
+ return value;
183
+ };
184
+
185
+ interface PickerConfig {
186
+ baseType: number;
187
+ format: string;
188
+ bodyClass: string;
189
+ tools: boolean;
190
+ }
191
+
192
+ const PICKER_CONFIGS: Record<string, PickerConfig> = {
193
+ date: { baseType: 0, format: 'YYYY-MM-DD', bodyClass: 'min-h-ls+', tools: true },
194
+ month: { baseType: 1, format: 'YYYY-MM', bodyClass: 'min-h-ms', tools: false },
195
+ year: { baseType: 2, format: 'YYYY', bodyClass: 'min-h-ms', tools: false },
196
+ };
161
197
 
162
198
  // 创建日期
163
199
  const cDate = (date: any = null) => {
164
- return isDateString(date) ? new Date(date) : new Date();
200
+ const normalized = normalizeDateInput(date);
201
+ return isDateString(normalized) ? new Date(normalized) : new Date();
165
202
  };
166
203
 
167
204
  // 快捷预设
@@ -198,7 +235,7 @@
198
235
  tools?: boolean;
199
236
  range?: boolean;
200
237
  format?: string;
201
- picker?: 'date' | 'week' | 'month' | 'quarter';
238
+ picker?: 'date' | 'week' | 'month' | 'quarter' | 'year';
202
239
  holiday?: object;
203
240
  shortcuts?: Array<'yesterday' | 'today' | 'tomorrow'>;
204
241
  extras?: (item: any) => string;
@@ -211,19 +248,19 @@
211
248
  // 是否显示框架
212
249
  frame: false,
213
250
  // 是否是示工具
214
- tools: true,
251
+ tools: undefined,
215
252
  // 时间范围
216
253
  range: false,
217
254
  // 面板类型
218
255
  picker: 'date',
219
256
  // 额外内容
220
257
  extras: (item: any) => {
221
- return `<i class="dib o-ms mb-xs r-xl ${item.today ? 'bg-main' : 'bg-none'}" style="width:.6em; height:.6em;"></i>`;
258
+ return `<i class="dib o-ms mb-xs r-xl ${item.today ? 'bg-main' : 'bg-none'}" style="width:.5em; height:.5em;"></i>`;
222
259
  },
223
260
  // 快捷选择
224
261
  shortcuts: () => ['yesterday', 'today', 'tomorrow'],
225
262
  // 数据格式化
226
- format: 'YYYY-MM-DD',
263
+ format: undefined,
227
264
  // 节假日
228
265
  holiday: () => ({}),
229
266
  time: false,
@@ -231,6 +268,11 @@
231
268
  lang: 'zh-CN',
232
269
  });
233
270
 
271
+ const pickerConfig = computed<PickerConfig>(() => {
272
+ const key = props.picker || 'date';
273
+ return PICKER_CONFIGS[key] || PICKER_CONFIGS.date;
274
+ });
275
+
234
276
  // 小时
235
277
  const hours = computed(() => {
236
278
  const _hours: any = [];
@@ -248,6 +290,27 @@
248
290
  return _seconds;
249
291
  });
250
292
 
293
+ const baseType = computed(() => pickerConfig.value.baseType);
294
+
295
+ const panelFormat = computed(() => props.format || pickerConfig.value.format);
296
+
297
+ const bodyClass = computed(() => pickerConfig.value.bodyClass);
298
+
299
+ const showTools = computed(() => {
300
+ if (props.tools !== undefined) {
301
+ return !!props.tools;
302
+ }
303
+ return pickerConfig.value.tools;
304
+ });
305
+
306
+ const isSelectedYear = (year: number) => {
307
+ return !!state.panel && state.curr?.y === year;
308
+ };
309
+
310
+ const isSelectedMonth = (month: number) => {
311
+ return !!state.panel && state.curr?.M === month;
312
+ };
313
+
251
314
  // 内部数据
252
315
  const state: any = reactive({
253
316
  // 面板显示时间
@@ -256,7 +319,6 @@
256
319
  years: 0,
257
320
  // 当前时间计算
258
321
  curr: computed(() => {
259
- // console.log(158, state.panel);
260
322
  const date = idate(state.panel);
261
323
  const days = date.calendar({ group: true, value: [state.panel] });
262
324
  return { days, ...date.attr };
@@ -281,6 +343,28 @@
281
343
  }),
282
344
  });
283
345
 
346
+ watch(
347
+ () => baseType.value,
348
+ (value) => {
349
+ state.type = value;
350
+ },
351
+ { immediate: true },
352
+ );
353
+
354
+ watch(
355
+ () => panelFormat.value,
356
+ (format) => {
357
+ if (state.panel) {
358
+ const formatted = idate(state.panel).format(format);
359
+ if (formatted !== state.panel) {
360
+ state.panel = formatted;
361
+ }
362
+ state.value = formatted;
363
+ }
364
+ },
365
+ { immediate: true },
366
+ );
367
+
284
368
  // 面板
285
369
  const panel: any = reactive({
286
370
  years: computed(() => {
@@ -301,9 +385,18 @@
301
385
 
302
386
  // 设置日期
303
387
  const setDateValue = (type: any, value: number = 0, add: number = 0) => {
304
- state.panel = upload(type, value, add);
388
+ const panelValue = upload(type, value, add);
389
+ state.panel = panelValue;
390
+ state.value = panelValue;
305
391
  // 面板返回上一级
306
- state.type -= 1;
392
+ const base = baseType.value;
393
+ const nextType = Math.max(base, state.type - 1);
394
+ state.type = nextType;
395
+
396
+ if (base > 0 && nextType === base) {
397
+ emits('change', panelValue);
398
+ update();
399
+ }
307
400
  };
308
401
 
309
402
  // 时分秒
@@ -338,10 +431,11 @@
338
431
  watch(
339
432
  () => props.modelValue,
340
433
  (value: any) => {
434
+ const formatted = value ? idate(value).format(panelFormat.value) : undefined;
341
435
  // 面板显示日期
342
- state.panel = value || undefined;
436
+ state.panel = formatted;
343
437
  // 值
344
- state.value = value || undefined;
438
+ state.value = formatted;
345
439
  },
346
440
  { deep: true, immediate: true },
347
441
  );
@@ -406,7 +500,7 @@
406
500
  default:
407
501
  break;
408
502
  }
409
- return idate(date).format(props.format);
503
+ return idate(date).format(panelFormat.value);
410
504
  };
411
505
 
412
506
  // 日期前翻
@@ -420,8 +514,10 @@
420
514
 
421
515
  // 向外通信
422
516
  const change = (item: any) => {
423
- state.panel = item ? idate(item.date).format(props.format) : item;
424
- emits('change', state.value);
517
+ const panelValue = item ? idate(item.date).format(panelFormat.value) : item;
518
+ state.panel = panelValue;
519
+ state.value = panelValue;
520
+ emits('change', panelValue);
425
521
  if (state.isNoTime) {
426
522
  update();
427
523
  }
@@ -430,18 +526,24 @@
430
526
  // 清空日期
431
527
  const remove = () => {
432
528
  state.panel = undefined;
529
+ state.value = undefined;
433
530
  update();
434
531
  };
435
532
 
436
533
  // 更新数据
437
534
  const update = () => {
438
- emits('update:modelValue', state.panel ? idate(state.panel).format(props.format) : undefined);
439
- emits('update', state.panel ? idate(state.panel).format(props.format) : undefined);
535
+ const nextValue = state.panel ? idate(state.panel).format(panelFormat.value) : undefined;
536
+ if (state.panel && nextValue !== state.panel) {
537
+ state.panel = nextValue;
538
+ }
539
+ state.value = nextValue;
540
+ emits('update:modelValue', nextValue);
541
+ emits('update', nextValue);
440
542
  };
441
543
 
442
544
  // 面板类型
443
545
  const setPanelType = () => {
444
- if (state.type < 3) {
546
+ if (state.type < 2) {
445
547
  state.type += 1;
446
548
  }
447
549
  };
@@ -456,16 +558,18 @@
456
558
  _timer.value = setTimeout(() => {
457
559
  const h = item.offsetHeight;
458
560
  const idx = Math.floor(ev.target.scrollTop / h);
561
+ const maxIdx = type === 'h' ? 23 : 59;
562
+ const safeIdx = Math.min(Math.max(idx, 0), maxIdx);
459
563
  const { YYYY = 0, MM = 0, DD = 0, HH = '', mm = '', ss = '' }: any = state.curr || {};
460
564
  let _panel = `${YYYY}-${MM}-${DD}`;
461
565
  if (type === 'h') {
462
- _panel += ` ${idx}:${mm}:${ss}`;
566
+ _panel += ` ${safeIdx}:${mm}:${ss}`;
463
567
  }
464
568
  if (type === 'm') {
465
- _panel += ` ${HH}:${idx}:${ss}`;
569
+ _panel += ` ${HH}:${safeIdx}:${ss}`;
466
570
  }
467
571
  if (type === 's') {
468
- _panel += ` ${HH}:${mm}:${idx}`;
572
+ _panel += ` ${HH}:${mm}:${safeIdx}`;
469
573
  }
470
574
  state.panel = idate(_panel).format('YYYY-MM-DD HH:mm:ss');
471
575
  }, 100);
@@ -37,18 +37,18 @@
37
37
 
38
38
  const props: any = defineProps({
39
39
  area: {
40
- type: HTMLElement,
41
- default: document.body
40
+ type: [Object, HTMLElement],
41
+ default: () => (typeof document !== 'undefined' ? document.body : null),
42
42
  },
43
43
  data: {
44
44
  type: Object,
45
45
  required: true,
46
- default: () => ({})
46
+ default: () => ({}),
47
47
  },
48
48
  form: {
49
49
  type: Object,
50
- default: () => ({})
51
- }
50
+ default: () => ({}),
51
+ },
52
52
  });
53
53
 
54
54
  const state: any = reactive({
@@ -108,7 +108,7 @@
108
108
  core(edit, form, '#form-field');
109
109
  error.shift();
110
110
  return error;
111
- })
111
+ }),
112
112
  });
113
113
  const { verify = [] }: any = toRefs(state);
114
114
 
@@ -124,16 +124,17 @@
124
124
  state.show = 0;
125
125
  }, 300);
126
126
  }
127
- }
127
+ },
128
128
  );
129
129
 
130
130
  const fScrollToField = (path: any) => {
131
+ if (typeof document === 'undefined') return;
131
132
  const dom = document.querySelector(path);
132
133
  if (dom) {
133
134
  scrollTo({
134
135
  el: props.area,
135
136
  to: {
136
- y: dom
137
+ y: dom,
137
138
  },
138
139
  offset: { y: -36 },
139
140
  then() {
@@ -141,7 +142,7 @@
141
142
  if (input) {
142
143
  input?.focus();
143
144
  }
144
- }
145
+ },
145
146
  });
146
147
  }
147
148
  };
@@ -169,7 +170,7 @@
169
170
  state.show = 0;
170
171
  }, 1000);
171
172
  }
172
- }
173
+ },
173
174
  );
174
175
 
175
176
  // 初始化后进入页面时自动弹出
@@ -191,7 +192,7 @@
191
192
  }, 300);
192
193
  }
193
194
  }
194
- }
195
+ },
195
196
  );
196
197
 
197
198
  watch(
@@ -200,7 +201,7 @@
200
201
  state.__update = !state.__update;
201
202
  },
202
203
  {
203
- deep: true
204
- }
204
+ deep: true,
205
+ },
205
206
  );
206
207
  </script>