@neatui/vue 2.17.5 → 3.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 (29) hide show
  1. package/.prettierrc +12 -0
  2. package/README.md +82 -0
  3. package/package.json +7 -4
  4. package/src/components/basic/IFollowView.vue +47 -34
  5. package/src/components/basic/IPickerView.vue +16 -8
  6. package/src/components/basic/IRouterView.vue +133 -74
  7. package/src/components/basic/IScrollView.vue +40 -28
  8. package/src/components/basic/LayerView/Layer.vue +33 -24
  9. package/src/components/basic/LayerView/index.ts +3 -1
  10. package/src/components/form/DateView@v3.vue +2 -1
  11. package/src/components/form/Input@v3.vue +9 -6
  12. package/src/components/form/TextArea.vue +3 -3
  13. package/src/components/loader/FormLoader/FormLoader.vue +29 -4
  14. package/src/components/loader/LayerLoader/LayerLoader.vue +2 -1
  15. package/src/components/loader/LimitLoader/LimitLoader@v3.vue +3 -3
  16. package/src/components/loader/TableLoader/TableLoader.vue +3 -3
  17. package/src/components/loader/index.ts +1 -1
  18. package/src/components/tools/FormDraftsView.vue +15 -11
  19. package/src/components/tools/FormVerifyView.vue +4 -3
  20. package/src/components/tools/MoreTools.vue +4 -3
  21. package/src/components/tools/Pagination@a.vue +74 -38
  22. package/src/components/tools/Pagination@b.vue +2 -1
  23. package/src/store/frame.ts +18 -10
  24. package/src/utils/theme.ts +3 -0
  25. package/src/components/basic/follow.ts +0 -133
  26. package/src/components/loader/FormLoader/FormLoader@v2.vue +0 -422
  27. package/src/components/loader/FormLoader/NodeLoader.vue +0 -85
  28. package/src/components/loader/FormLoader@v1/FormLoader.vue +0 -506
  29. package/src/components/loader/FormLoader@v1/FormRender.vue +0 -302
@@ -1,9 +1,9 @@
1
1
  <template>
2
- <div :am-view="view" :am-mode="mode" class="iroute-view pr full" :view-full="full ? 1 : 0" @animationend="anim">
2
+ <div :am-view="view" :am-mode="mode" :am-animate="transitioning ? 1 : 0" class="iroute-view pr full" :view-full="full ? 1 : 0" @animationend="anim">
3
3
  <div :class="listClass" :style="listStyle" am-view-item="list">
4
4
  <slot name="list" :mode="mode"></slot>
5
5
  </div>
6
- <div v-if="view === 'iteming' || view === 'listing' || view === 'item'" ref="itemRef" :class="itemClass" :style="itemStyle" am-view-item="item">
6
+ <div v-if="itemEverMounted" 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>
@@ -12,7 +12,7 @@
12
12
  </div>
13
13
  </template>
14
14
  <script setup lang="ts">
15
- import { deepcopy } from '@fekit/utils';
15
+ import { deepcopy, env } from '@fekit/utils';
16
16
  import { ref, watch, provide, reactive } from 'vue';
17
17
  import { useRoute, useRouter } from 'vue-router';
18
18
  const route = useRoute();
@@ -21,6 +21,7 @@
21
21
  interface Props {
22
22
  full?: boolean | 0 | 1;
23
23
  once?: number;
24
+ animate?: boolean | 0 | 1;
24
25
  mode?: 'default' | 'preview' | 'pop-up' | 'window';
25
26
  queryName?: string;
26
27
  listClass?: string;
@@ -31,6 +32,7 @@
31
32
  const props: any = withDefaults(defineProps<Props>(), {
32
33
  full: 0,
33
34
  once: 0,
35
+ animate: 1,
34
36
  mode: 'default',
35
37
  queryName: 'id',
36
38
  listClass: 'full',
@@ -39,64 +41,90 @@
39
41
  itemStyle: '',
40
42
  });
41
43
  const state: any = reactive({
42
- first: 1,
43
44
  list: null,
44
45
  item: null,
45
46
  });
46
47
 
47
48
  const evs = (v: any = 'list', p: any = 'item') => {
48
49
  return (then: any = () => {}, param: any) => {
50
+ if (!env.client) return;
51
+ // 注册时已在目标视图、且对侧没留数据 → 视为初次挂载噪音,吞掉一次
52
+ // 对侧已经写入 state[v](如 item 退出前调用了 onitem(param))→ 不跳过,正常派发
53
+ let skipInitial = view.value === v && state[v] == null;
49
54
  watch(
50
55
  () => view.value,
51
56
  () => {
52
- if (view.value === v) {
53
- if (param) {
54
- state[p] = param;
55
- }
56
- if (!state.first) {
57
- then({ view: view.value, mode: props.mode, param: deepcopy(state[v] || {}) });
58
- } else {
59
- state.first = 0;
60
- }
57
+ if (view.value !== v) return;
58
+ if (skipInitial) {
59
+ skipInitial = false;
60
+ // 初始 list 仍写入 state.item:item 首次进入能拿到 list 的上下文。
61
+ // 初始 item 不写入 state.list:避免 deep-link 仅靠 item slot 挂载
62
+ // 就给 list 留下假的 "edit" 信号、cancel 回 list 触发误刷新。
63
+ if (v === 'list' && param) state[p] = param;
64
+ return;
61
65
  }
66
+ if (param) state[p] = param;
67
+ then({ view: view.value, mode: props.mode, param: deepcopy(state[v] || {}) });
62
68
  },
63
- { deep: true, immediate: true },
69
+ { immediate: true },
64
70
  );
65
71
  };
66
72
  };
67
73
 
74
+ const hasItemQuery = () => {
75
+ const value = route.query[props.queryName];
76
+ if (Array.isArray(value)) return value.some((item) => item !== undefined && item !== null && item !== '');
77
+ return value !== undefined && value !== null && value !== '';
78
+ };
79
+
68
80
  const init = ref(0);
69
- const view = ref('');
81
+ const currentHasItem = ref(hasItemQuery());
82
+ const transitioning = ref(false);
83
+ const view = ref(currentHasItem.value ? 'item' : 'list');
84
+ // item slot 只在首次进入 item 视图后才挂载,之后始终保持,保留表单状态等
85
+ // list slot 则始终挂载(模板无 v-if),保留勾选/滚动/筛选
86
+ const itemEverMounted = ref(view.value === 'item' || view.value === 'iteming' || view.value === 'listing');
87
+ watch(
88
+ () => view.value,
89
+ (v) => {
90
+ if (v === 'item' || v === 'iteming' || v === 'listing') itemEverMounted.value = true;
91
+ },
92
+ );
93
+ const showAnimations = ['iroute-show-default', 'iroute-show-preview', 'iroute-show-pop-up', 'iroute-show-window'];
94
+ const hideAnimations = ['iroute-hide-default', 'iroute-hide-preview', 'iroute-hide-pop-up', 'iroute-hide-window'];
70
95
  const anim = (e: any) => {
71
- if (
72
- e.animationName === 'am-show-default' ||
73
- e.animationName === 'am-show-window' ||
74
- e.animationName === 'am-show-preview' ||
75
- e.animationName === 'am-show-pop-up' ||
76
- e.animationName === 'am-hide-default' ||
77
- e.animationName === 'am-hide-window' ||
78
- e.animationName === 'am-hide-preview' ||
79
- e.animationName === 'am-hide-pop-up'
80
- ) {
81
- view.value = route.query[props.queryName] ? 'item' : 'list';
82
- }
96
+ const isShow = showAnimations.includes(e.animationName);
97
+ const isHide = hideAnimations.includes(e.animationName);
98
+ if (!isShow && !isHide) return;
99
+
100
+ currentHasItem.value = hasItemQuery();
101
+ view.value = currentHasItem.value ? 'item' : 'list';
102
+ if (isShow || props.mode !== 'default') transitioning.value = false;
83
103
  };
84
104
 
85
105
  const a = () => {
106
+ const nextHasItem = hasItemQuery();
86
107
  if (init.value) {
87
108
  if (!props.once) {
88
- view.value = route.query[props.queryName] ? 'iteming' : 'listing';
109
+ const shouldAnimate = !!props.animate && nextHasItem !== currentHasItem.value;
110
+ currentHasItem.value = nextHasItem;
111
+ transitioning.value = shouldAnimate;
112
+ view.value = shouldAnimate ? (nextHasItem ? 'iteming' : 'listing') : nextHasItem ? 'item' : 'list';
89
113
  }
90
114
  } else {
91
- view.value = route.query[props.queryName] ? 'item' : 'list';
115
+ currentHasItem.value = nextHasItem;
116
+ transitioning.value = false;
117
+ view.value = nextHasItem ? 'item' : 'list';
92
118
  init.value = 1;
93
119
  }
94
120
  };
95
121
 
96
122
  provide('onlist', evs('list', 'item'));
97
123
  provide('onitem', evs('item', 'list'));
98
- watch(() => route.query[props.queryName], a);
99
- a();
124
+ if (env.client) {
125
+ watch(() => route.query[props.queryName], a);
126
+ a();
127
+ }
100
128
 
101
129
  // 取消
102
130
  const cancel = () => {
@@ -125,18 +153,15 @@
125
153
  width: 100%;
126
154
  height: 100%;
127
155
  & > * {
128
- pointer-events: none;
129
- & > * {
130
- pointer-events: auto;
131
- }
156
+ pointer-events: auto;
132
157
  }
133
158
  }
134
159
 
135
160
  // 默认模式
136
- [am-view][am-mode~='default'] {
161
+ .iroute-view[am-mode~='default'] {
137
162
  overflow: hidden;
138
163
 
139
- @keyframes am-show-default {
164
+ @keyframes iroute-show-default {
140
165
  0% {
141
166
  opacity: 0;
142
167
  transform: translate3d(1em, 0, 0);
@@ -146,7 +171,7 @@
146
171
  transform: translate3d(0, 0, 0);
147
172
  }
148
173
  }
149
- @keyframes am-hide-default {
174
+ @keyframes iroute-hide-default {
150
175
  0% {
151
176
  opacity: 1;
152
177
  transform: translate3d(0, 0, 0);
@@ -177,39 +202,47 @@
177
202
  & > [am-view-item~='list'] {
178
203
  flex-direction: column;
179
204
  display: flex;
180
- animation: am-show-default 0.35s ease both;
205
+ }
206
+ }
207
+ &[am-animate='1'][am-view='list'] {
208
+ & > [am-view-item~='list'] {
209
+ animation: iroute-show-default 0.35s ease both;
181
210
  }
182
211
  }
183
212
  &[am-view='listing'] {
184
213
  & > [am-view-item~='item'] {
185
214
  flex-direction: column;
186
215
  display: flex;
187
- animation: am-hide-default 0.3s ease-in-out both;
216
+ animation: iroute-hide-default 0.3s ease-in-out both;
188
217
  }
189
218
  }
190
219
  &[am-view='item'] {
191
220
  & > [am-view-item~='item'] {
192
221
  display: flex;
193
- animation: am-show-default 0.35s ease both;
222
+ }
223
+ }
224
+ &[am-animate='1'][am-view='item'] {
225
+ & > [am-view-item~='item'] {
226
+ animation: iroute-show-default 0.35s ease both;
194
227
  }
195
228
  }
196
229
  &[am-view='iteming'] {
197
230
  & > [am-view-item~='list'] {
198
231
  display: flex;
199
- animation: am-hide-default 0.3s ease-in-out both;
232
+ animation: iroute-hide-default 0.3s ease-in-out both;
200
233
  }
201
234
  }
202
235
  }
203
236
 
204
- // 默认模式
205
- [am-view][am-mode~='preview'] {
237
+ // 预览模式
238
+ .iroute-view[am-mode~='preview'] {
206
239
  overflow: hidden;
207
240
 
208
241
  .iroute-view-body {
209
242
  justify-content: end;
210
243
  }
211
244
 
212
- @keyframes am-show-preview {
245
+ @keyframes iroute-show-preview {
213
246
  0% {
214
247
  opacity: 0;
215
248
  transform: translate3d(110%, 0, 0);
@@ -219,7 +252,7 @@
219
252
  transform: translate3d(0, 0, 0);
220
253
  }
221
254
  }
222
- @keyframes am-hide-preview {
255
+ @keyframes iroute-hide-preview {
223
256
  0% {
224
257
  opacity: 1;
225
258
  transform: translate3d(0, 0, 0);
@@ -256,18 +289,27 @@
256
289
  pointer-events: none;
257
290
  }
258
291
  }
259
- &[am-view='list'],
292
+ // list 视图:item 即便始终挂载也要隐藏(无 v-if 时需 CSS 兜底)
293
+ &[am-view='list'] {
294
+ & > [am-view-item~='item'] {
295
+ display: none;
296
+ }
297
+ }
260
298
  &[am-view='listing'] {
261
299
  & > [am-view-item~='item'] {
262
300
  display: flex;
263
- animation: am-hide-preview 0.5s ease-in-out both;
301
+ animation: iroute-hide-preview 0.5s ease-in-out both;
302
+ }
303
+ }
304
+ &[am-view='item'] {
305
+ & > [am-view-item~='item'] {
306
+ display: flex;
264
307
  }
265
308
  }
266
- &[am-view='item'],
267
309
  &[am-view='iteming'] {
268
310
  & > [am-view-item~='item'] {
269
311
  display: flex;
270
- animation: am-show-preview 0.5s ease both;
312
+ animation: iroute-show-preview 0.5s ease both;
271
313
  }
272
314
  }
273
315
 
@@ -284,7 +326,7 @@
284
326
  }
285
327
 
286
328
  // 弹出窗口
287
- [am-view][am-mode~='pop-up'] {
329
+ .iroute-view[am-mode~='pop-up'] {
288
330
  overflow: hidden;
289
331
 
290
332
  .iroute-view-body {
@@ -297,7 +339,7 @@
297
339
  pointer-events: none;
298
340
  }
299
341
 
300
- @keyframes am-show-pop-up {
342
+ @keyframes iroute-show-pop-up {
301
343
  0% {
302
344
  opacity: 0;
303
345
  transform: translate3d(0, 0, 0) scale(0.95);
@@ -307,7 +349,7 @@
307
349
  transform: translate3d(0, 0, 0);
308
350
  }
309
351
  }
310
- @keyframes am-hide-pop-up {
352
+ @keyframes iroute-hide-pop-up {
311
353
  0% {
312
354
  opacity: 1;
313
355
  transform: translate3d(0, 0, 0);
@@ -347,50 +389,56 @@
347
389
  transition: all 0.35s;
348
390
  }
349
391
  }
350
- &[am-view='list'],
392
+ // list 视图:item 即便始终挂载也要隐藏(无 v-if 时需 CSS 兜底)
393
+ &[am-view='list'] {
394
+ & > [am-view-item~='item'] {
395
+ display: none;
396
+ }
397
+ }
351
398
  &[am-view='listing'] {
352
399
  & > [am-view-item~='item'] {
353
400
  display: flex;
354
401
  opacity: 0;
355
402
  & > * {
356
- animation: am-hide-pop-up 0.35s ease both;
403
+ animation: iroute-hide-pop-up 0.35s ease both;
357
404
  }
358
405
  }
359
406
  }
360
- &[am-view='item'],
407
+ &[am-view='item'] {
408
+ & > [am-view-item~='item'] {
409
+ display: flex;
410
+ opacity: 1;
411
+ }
412
+ }
361
413
  &[am-view='iteming'] {
362
414
  & > [am-view-item~='item'] {
363
415
  display: flex;
364
416
  opacity: 1;
365
417
  & > * {
366
- animation: am-show-pop-up 0.35s ease both;
418
+ animation: iroute-show-pop-up 0.35s ease both;
367
419
  }
368
420
  }
369
421
  }
370
422
  }
371
423
 
372
- // 窗口模式
373
- [am-view][am-mode~='window'] {
424
+ // 窗口模式:item 撑满整个浏览器窗口的全屏 overlay,行为类似 pop-up 但内容撑满
425
+ .iroute-view[am-mode~='window'] {
374
426
  overflow: hidden;
375
427
 
376
- @keyframes am-show-default {
428
+ @keyframes iroute-show-window {
377
429
  0% {
378
430
  opacity: 0;
379
- transform: translate3d(1em, 0, 0);
380
431
  }
381
432
  100% {
382
433
  opacity: 1;
383
- transform: translate3d(0, 0, 0);
384
434
  }
385
435
  }
386
- @keyframes am-hide-default {
436
+ @keyframes iroute-hide-window {
387
437
  0% {
388
438
  opacity: 1;
389
- transform: translate3d(0, 0, 0);
390
439
  }
391
440
  100% {
392
441
  opacity: 0;
393
- transform: translate3d(1em, 0, 0);
394
442
  }
395
443
  }
396
444
 
@@ -409,31 +457,42 @@
409
457
  &[am-view-item~='row'] {
410
458
  flex-direction: row !important;
411
459
  }
460
+
461
+ &[am-view-item~='item'] {
462
+ position: fixed;
463
+ top: 0;
464
+ right: 0;
465
+ bottom: 0;
466
+ left: 0;
467
+ z-index: 100;
468
+ pointer-events: none;
469
+ }
412
470
  }
413
471
  &[am-view='list'] {
414
- [am-view-item~='list'] {
472
+ & > [am-view-item~='list'] {
415
473
  flex-direction: column;
416
474
  display: flex;
417
- animation: am-show-default 0s ease both;
418
475
  }
419
476
  }
420
477
  &[am-view='listing'] {
421
- [am-view-item~='item'] {
422
- flex-direction: column;
478
+ & > [am-view-item~='item'] {
423
479
  display: flex;
424
- animation: am-hide-default 0s ease-in-out both;
480
+ & > * {
481
+ animation: iroute-hide-window 0.3s ease both;
482
+ }
425
483
  }
426
484
  }
427
485
  &[am-view='item'] {
428
- [am-view-item~='item'] {
486
+ & > [am-view-item~='item'] {
429
487
  display: flex;
430
- animation: am-show-default 0s ease both;
431
488
  }
432
489
  }
433
490
  &[am-view='iteming'] {
434
- [am-view-item~='list'] {
491
+ & > [am-view-item~='item'] {
435
492
  display: flex;
436
- animation: am-hide-default 0s ease-in-out both;
493
+ & > * {
494
+ animation: iroute-show-window 0.3s ease both;
495
+ }
437
496
  }
438
497
  }
439
498
  }
@@ -4,7 +4,7 @@
4
4
  :ui-scroll="`:y ${stable ? 'stable' : ''}`"
5
5
  ref="root"
6
6
  >
7
- <div class="full">
7
+ <div>
8
8
  <div class="fs-xs co-text o-mm" fekit-pullload-head="" v-if="pull">
9
9
  <slot name="pull-head" :state="demo.store.pull">
10
10
  <div v-show="demo.store.pull < 2" ui-flex="row cm">
@@ -37,7 +37,6 @@
37
37
  </template>
38
38
  <script setup lang="ts">
39
39
  import { onMounted, onBeforeUnmount, ref } from "vue";
40
- import PullLoad, { Easing } from "@fekit/pullload";
41
40
  const emits = defineEmits(["onpull", "onload"]);
42
41
  const props: any = defineProps({
43
42
  pull: {
@@ -54,42 +53,55 @@ const props: any = defineProps({
54
53
  },
55
54
  });
56
55
  const root: any = ref(null);
57
- const demo: any = ref(null);
58
- demo.value = new PullLoad({
59
- easing: Easing.easeOut,
60
- friction: 0.3,
61
- triggerDistance: 60,
62
- loadingDistance: 50,
63
- ...(props.pull
64
- ? {
65
- onpull: async () => {
66
- emits("onpull", demo.value);
67
- },
68
- }
69
- : {}),
70
- ...(props.load
71
- ? {
72
- onload: async () => {
73
- emits("onload", demo.value);
74
- },
75
- }
76
- : {}),
77
- });
56
+ const demo: any = ref({ store: { pull: 0, load: 0 } });
57
+ let alive = true;
58
+
59
+ const createPullLoad = async () => {
60
+ const { default: PullLoad, Easing } = await import("@fekit/pullload");
61
+ return new PullLoad({
62
+ easing: Easing.easeOut,
63
+ friction: 0.3,
64
+ triggerDistance: 60,
65
+ loadingDistance: 50,
66
+ ...(props.pull
67
+ ? {
68
+ onpull: async () => {
69
+ emits("onpull", demo.value);
70
+ },
71
+ }
72
+ : {}),
73
+ ...(props.load
74
+ ? {
75
+ onload: async () => {
76
+ emits("onload", demo.value);
77
+ },
78
+ }
79
+ : {}),
80
+ });
81
+ };
82
+
83
+ onMounted(async () => {
84
+ const pullLoad = await createPullLoad();
85
+ if (!alive) {
86
+ pullLoad?.remove?.();
87
+ return;
88
+ }
78
89
 
79
- onMounted(() => {
90
+ demo.value = pullLoad;
80
91
  if (root.value) {
81
- demo.value?.listen(root.value);
92
+ demo.value?.listen?.(root.value);
82
93
  if (props.pull) {
83
- demo.value?.ispull();
94
+ demo.value?.ispull?.();
84
95
  }
85
96
  }
86
97
  });
87
98
  onBeforeUnmount(() => {
88
- demo.value?.remove();
99
+ alive = false;
100
+ demo.value?.remove?.();
89
101
  });
90
102
 
91
103
  const pullload = () => {
92
- demo.value?.ispull();
104
+ demo.value?.ispull?.();
93
105
  };
94
106
 
95
107
  defineExpose({ pullload, ex: () => demo.value });
@@ -1,29 +1,34 @@
1
1
  <!-- 实验性插件 -->
2
2
  <template>
3
- <Teleport :to="area">
4
- <div
5
- v-if="state.view"
6
- :am-view="am"
7
- :data-key="id"
8
- :style="`${`${dark}` ? 'background-color: rgba(0, 0, 0, ' + dark + ');' : ''}${zIndex ? 'z-index:' + zIndex + ';' : ''}`"
9
- :view="state.view"
10
- class="fekit-layer"
11
- >
12
- <div :ui-flex="`col ${flex}`" :style="wrapStyle" class="fekit-layer-wrap" @animationend="anim" @click="_sys_hide">
13
- <slot :param="_tasks[id]?.params"></slot>
3
+ <div>
4
+ <Teleport :to="area">
5
+ <div
6
+ v-if="state.view"
7
+ :am-view="am"
8
+ :data-key="id"
9
+ :style="`${`${dark}` ? 'background-color: rgba(0, 0, 0, ' + dark + ');' : ''}${zIndex ? 'z-index:' + zIndex + ';' : ''}`"
10
+ :view="state.view"
11
+ class="fekit-layer"
12
+ >
13
+ <div :ui-flex="`col ${flex}`" :style="wrapStyle" class="fekit-layer-wrap" @animationend="anim" @click="_sys_hide">
14
+ <slot :param="_tasks[id]?.params"></slot>
15
+ </div>
14
16
  </div>
15
- </div>
16
- </Teleport>
17
+ </Teleport>
18
+ </div>
17
19
  </template>
18
20
  <script lang="ts" setup>
19
21
  import { onBeforeUnmount, reactive, watch } from 'vue';
20
- import { isFunction } from '@fekit/utils';
22
+ import { env, isFunction } from '@fekit/utils';
21
23
 
22
24
  // 弹层队列
23
- if (!(window as any)._FEKIT_LAYER_TASKS) {
24
- (window as any)._FEKIT_LAYER_TASKS = {};
25
- }
26
- const _tasks: any = (window as any)._FEKIT_LAYER_TASKS;
25
+ const _tasks: any = (() => {
26
+ if (!env.client) return {};
27
+ if (!(window as any)._FEKIT_LAYER_TASKS) {
28
+ (window as any)._FEKIT_LAYER_TASKS = {};
29
+ }
30
+ return (window as any)._FEKIT_LAYER_TASKS;
31
+ })();
27
32
 
28
33
  // 获取活跃的弹层
29
34
  const getActiveLayer = () => {
@@ -54,12 +59,14 @@
54
59
  wrapStyle: '',
55
60
  });
56
61
 
57
- const _area: any = document.querySelector(props.area);
58
- if (!_area) {
59
- const el = document.createElement('div');
60
- el.id = (props.area || '#fekit').substring(1);
61
- el.style.position = 'absolute';
62
- document.body.appendChild(el);
62
+ if (env.client) {
63
+ const _area: any = document.querySelector(props.area);
64
+ if (!_area) {
65
+ const el = document.createElement('div');
66
+ el.id = (props.area || '#fekit').substring(1);
67
+ el.style.position = 'absolute';
68
+ document.body.appendChild(el);
69
+ }
63
70
  }
64
71
 
65
72
  // 状态管理
@@ -71,6 +78,7 @@
71
78
  });
72
79
 
73
80
  function disableScroll() {
81
+ if (!env.client) return;
74
82
  state.top = document.documentElement.scrollTop;
75
83
  const sw = window.innerWidth - document.documentElement.clientWidth;
76
84
  state.sw = sw;
@@ -82,6 +90,7 @@
82
90
  }
83
91
 
84
92
  function enableScroll() {
93
+ if (!env.client) return;
85
94
  document.body.style.removeProperty('overflow');
86
95
  document.body.style.removeProperty('position');
87
96
  document.body.style.removeProperty('top');
@@ -1,5 +1,7 @@
1
1
  import Layer from './Layer.vue';
2
+ import { env } from '@fekit/utils';
3
+
2
4
  const LayerById = (id: any) => {
3
- return (window as any)._FEKIT_LAYER_TASKS[id];
5
+ return env.client ? (window as any)._FEKIT_LAYER_TASKS?.[id] : undefined;
4
6
  };
5
7
  export { Layer, LayerById };
@@ -217,7 +217,8 @@
217
217
  // 创建日期
218
218
  const cDate = (date: any = null) => {
219
219
  const normalized = normalizeDateInput(date);
220
- return isDateString(normalized) ? new Date(normalized) : new Date();
220
+ // iOS 兼容:Safari 不支持 "YYYY-MM-DD" 格式,需要替换为 "YYYY/MM/DD"
221
+ return isDateString(normalized) ? new Date(normalized?.replace(/-/g, '/')) : new Date();
221
222
  };
222
223
 
223
224
  // 快捷预设