@neatui/vue 4.4.0 → 4.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neatui/vue",
3
- "version": "4.4.0",
3
+ "version": "4.6.0",
4
4
  "description": "basic component ui",
5
5
  "main": "./src/index.ts",
6
6
  "license": "MIT",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@fekit/floor": "^3.0.1",
30
- "@fekit/follow": "^1.8.0",
30
+ "@fekit/follow": "^1.7.0",
31
31
  "@fekit/itable": "^1.1.6",
32
32
  "@fekit/pullload": "^1.3.5",
33
33
  "@fekit/scale": "^2.0.1",
@@ -56,7 +56,6 @@
56
56
  clickTipHide?: boolean;
57
57
  mobile?: boolean;
58
58
  title?: string;
59
- zIndex?: string | number | null;
60
59
  }
61
60
 
62
61
  // 入参
@@ -74,7 +73,6 @@
74
73
  clickTipHide: false,
75
74
  mobile: true,
76
75
  title: '',
77
- zIndex: undefined,
78
76
  });
79
77
 
80
78
  const dom: any = ref(null);
@@ -216,20 +214,13 @@
216
214
  }
217
215
  }
218
216
  },
219
- { hideOnScrollOut: false, zIndex: props.zIndex },
217
+ { hideOnScrollOut: false },
220
218
  );
221
219
  } else {
222
220
  ex.value = null;
223
221
  }
224
222
  });
225
223
 
226
- watch(
227
- () => props.zIndex,
228
- (zIndex) => {
229
- ex.value?.setOptions({ zIndex });
230
- },
231
- );
232
-
233
224
  onUnmounted(() => {
234
225
  document.removeEventListener('click', cancel, true);
235
226
  // 移除触摸事件监听器
@@ -6,11 +6,14 @@
6
6
  v-if="state.view"
7
7
  :am-view="am"
8
8
  :data-key="id"
9
- :style="`${`${dark}` ? 'background-color: rgba(0, 0, 0, ' + dark + ');' : ''}${zIndex ? 'z-index:' + zIndex + ';' : ''}`"
9
+ :data-recess="state.recess || undefined"
10
+ :data-cover-dark="state.coverDark !== '' ? '' : undefined"
11
+ :style="layerStyle"
10
12
  :view="state.view"
11
13
  class="fekit-layer"
12
14
  >
13
15
  <div :ui-flex="`col ${flex}`" :style="wrapStyle" class="fekit-layer-wrap" @animationend="anim" @click="_sys_hide">
16
+ <div v-if="state.coverDark !== ''" class="fekit-layer-cover-dark"></div>
14
17
  <slot :param="_tasks[id]?.params"></slot>
15
18
  </div>
16
19
  </div>
@@ -18,7 +21,7 @@
18
21
  </div>
19
22
  </template>
20
23
  <script lang="ts" setup>
21
- import { onBeforeUnmount, reactive, watch } from 'vue';
24
+ import { computed, nextTick, onBeforeUnmount, reactive, watch } from 'vue';
22
25
  import { env, isFunction } from '@fekit/utils';
23
26
 
24
27
  // 弹层队列
@@ -60,6 +63,21 @@
60
63
  const SCROLL_LOCK_STATE = Symbol.for('@neatui/vue.Layer.scrollLock');
61
64
  const ESCAPE_STACK = Symbol.for('@neatui/vue.Layer.escapeStack');
62
65
  const ESCAPE_LISTENER = Symbol.for('@neatui/vue.Layer.escapeListener');
66
+ const AREA_GROUPS = Symbol.for('@neatui/vue.Layer.areaGroups');
67
+
68
+ type LayerMode = 'cover' | 'cascade' | 'displace' | 'replace' | 'queue';
69
+
70
+ type AreaGroup = {
71
+ dark: {
72
+ afterHide?: () => void;
73
+ el: HTMLElement | null;
74
+ listening: boolean;
75
+ owners: Set<string>;
76
+ view: number;
77
+ };
78
+ queue: Array<{ id: string; kind: 'open' | 'show'; params: any; then: any }>;
79
+ stack: string[];
80
+ };
63
81
 
64
82
  const getScrollLockState = (): ScrollLockState => {
65
83
  if (!_tasks[SCROLL_LOCK_STATE]) {
@@ -119,6 +137,199 @@
119
137
  });
120
138
  };
121
139
 
140
+ const getAreaGroups = (): Record<string, AreaGroup> => {
141
+ if (!_tasks[AREA_GROUPS]) {
142
+ Object.defineProperty(_tasks, AREA_GROUPS, {
143
+ configurable: true,
144
+ value: {},
145
+ });
146
+ }
147
+ return _tasks[AREA_GROUPS];
148
+ };
149
+
150
+ const getAreaGroup = (area: string): AreaGroup => {
151
+ const groups = getAreaGroups();
152
+ if (!groups[area]) {
153
+ groups[area] = {
154
+ dark: {
155
+ el: null,
156
+ listening: false,
157
+ owners: new Set<string>(),
158
+ view: 0,
159
+ },
160
+ queue: [],
161
+ stack: [],
162
+ };
163
+ }
164
+ return groups[area];
165
+ };
166
+
167
+ const resolveDarkOpacity = (value: string | number | undefined) => {
168
+ return `${value ?? ''}` === '' ? '0.75' : `${value}`;
169
+ };
170
+
171
+ const participatesInGroup = (dark: string | number | undefined) => `${dark ?? ''}` !== '0';
172
+ const DEFAULT_COVER_DARK = 0.32;
173
+
174
+ const resolveCoverDark = (mode: LayerMode, hasPrev: boolean, coverDark: unknown) => {
175
+ if (mode !== 'cover' || !hasPrev) return '';
176
+ if (coverDark === false || `${coverDark}` === '0') return '';
177
+ if (coverDark === undefined || coverDark === true || `${coverDark}` === '') return DEFAULT_COVER_DARK;
178
+ return coverDark as string | number;
179
+ };
180
+
181
+ const isBlockingView = (view: number) => view === 1 || view === 2 || view === 3;
182
+
183
+ const occupiesGlobalDark = (task: any) => {
184
+ if (!task?.participates) return false;
185
+ if (task.view === 2 || task.view === 3 || task.view === 4) return true;
186
+ return task.view === 1 && !!task.parkAfterHide;
187
+ };
188
+
189
+ const setGlobalDarkView = (group: AreaGroup, view: number) => {
190
+ group.dark.view = view;
191
+ if (group.dark.el) group.dark.el.setAttribute('view', `${view}`);
192
+ };
193
+
194
+ const ensureGlobalDarkEl = (area: string, group: AreaGroup) => {
195
+ if (!env.client) return null;
196
+ if (group.dark.el?.isConnected) return group.dark.el;
197
+ group.dark.el = null;
198
+ group.dark.listening = false;
199
+ const container = document.querySelector(area);
200
+ if (!container) return null;
201
+ let el = container.querySelector(':scope > .fekit-layer-dark') as HTMLElement | null;
202
+ if (!el) {
203
+ el = document.createElement('div');
204
+ el.className = 'fekit-layer-dark';
205
+ el.setAttribute('view', '0');
206
+ container.insertBefore(el, container.firstChild);
207
+ }
208
+ group.dark.el = el;
209
+ el.addEventListener('animationend', (ev) => {
210
+ if (ev.target !== el) return;
211
+ if (group.dark.view === 2) setGlobalDarkView(group, 3);
212
+ if (group.dark.view === 1) {
213
+ setGlobalDarkView(group, 0);
214
+ const afterHide = group.dark.afterHide;
215
+ group.dark.afterHide = undefined;
216
+ afterHide?.();
217
+ }
218
+ });
219
+ group.dark.listening = true;
220
+ return el;
221
+ };
222
+
223
+ const syncGlobalDarkOpacity = (area: string, group: AreaGroup) => {
224
+ let opacity = '0.75';
225
+ for (let index = group.stack.length - 1; index >= 0; index -= 1) {
226
+ const task = _tasks[group.stack[index]];
227
+ if (occupiesGlobalDark(task)) {
228
+ opacity = resolveDarkOpacity(task.dark);
229
+ break;
230
+ }
231
+ }
232
+ const el = group.dark.owners.size ? ensureGlobalDarkEl(area, group) : group.dark.el;
233
+ if (el) el.style.setProperty('--layer-dark', opacity);
234
+ };
235
+
236
+ const acquireGlobalDark = (area: string, group: AreaGroup, layerId: string, immediate = false) => {
237
+ if (!layerId) return;
238
+ if (group.dark.owners.has(layerId)) {
239
+ syncGlobalDarkOpacity(area, group);
240
+ return;
241
+ }
242
+ group.dark.owners.add(layerId);
243
+ const el = ensureGlobalDarkEl(area, group);
244
+ if (!el) return;
245
+ syncGlobalDarkOpacity(area, group);
246
+ if (group.dark.owners.size > 1) {
247
+ if (group.dark.view === 1) setGlobalDarkView(group, 3);
248
+ else if (group.dark.view === 0) setGlobalDarkView(group, immediate ? 3 : 2);
249
+ return;
250
+ }
251
+ if (immediate) {
252
+ setGlobalDarkView(group, 3);
253
+ return;
254
+ }
255
+ if (group.dark.view === 0) setGlobalDarkView(group, 2);
256
+ else if (group.dark.view === 1) setGlobalDarkView(group, 3);
257
+ };
258
+
259
+ const releaseGlobalDark = (area: string, group: AreaGroup, layerId: string, immediate = false) => {
260
+ if (!layerId || !group.dark.owners.delete(layerId)) {
261
+ syncGlobalDarkOpacity(area, group);
262
+ return;
263
+ }
264
+ syncGlobalDarkOpacity(area, group);
265
+ if (group.dark.owners.size > 0) return;
266
+ if (immediate || !group.dark.el) {
267
+ setGlobalDarkView(group, 0);
268
+ const afterHide = group.dark.afterHide;
269
+ group.dark.afterHide = undefined;
270
+ afterHide?.();
271
+ return;
272
+ }
273
+ if (group.dark.view === 0) return;
274
+ setGlobalDarkView(group, 1);
275
+ };
276
+
277
+ const visibleGroupIds = (group: AreaGroup) =>
278
+ group.stack.filter((layerId) => {
279
+ const view = _tasks[layerId]?.view;
280
+ return view === 2 || view === 3;
281
+ });
282
+
283
+ const groupHasBlocking = (group: AreaGroup, exceptId = '') =>
284
+ group.stack.some((layerId) => layerId !== exceptId && isBlockingView(_tasks[layerId]?.view));
285
+
286
+ const syncStackIndex = (group: AreaGroup) => {
287
+ group.stack.forEach((layerId, index) => {
288
+ _tasks[layerId]?.setStackIndex?.(index);
289
+ });
290
+ };
291
+
292
+ const recomputeRecess = (group: AreaGroup) => {
293
+ const visible = visibleGroupIds(group);
294
+ visible.forEach((layerId, index) => {
295
+ let depth = 0;
296
+ for (let next = index + 1; next < visible.length; next += 1) {
297
+ if (_tasks[visible[next]]?.openMode === 'cascade') depth += 1;
298
+ }
299
+ _tasks[layerId]?.setRecess?.(depth > 2 ? 2 : depth);
300
+ });
301
+ group.stack.forEach((layerId) => {
302
+ if (!visible.includes(layerId)) _tasks[layerId]?.setRecess?.(0);
303
+ });
304
+ };
305
+
306
+ const ensureOnStack = (group: AreaGroup, layerId: string) => {
307
+ const index = group.stack.indexOf(layerId);
308
+ if (index !== -1) group.stack.splice(index, 1);
309
+ group.stack.push(layerId);
310
+ syncStackIndex(group);
311
+ };
312
+
313
+ const removeFromStack = (group: AreaGroup, layerId: string) => {
314
+ const index = group.stack.indexOf(layerId);
315
+ if (index !== -1) group.stack.splice(index, 1);
316
+ syncStackIndex(group);
317
+ };
318
+
319
+ const removeFromQueue = (group: AreaGroup, layerId: string) => {
320
+ group.queue = group.queue.filter((job) => job.id !== layerId);
321
+ };
322
+
323
+ const getVisibleTop = (group: AreaGroup, exceptId = '') => {
324
+ for (let index = group.stack.length - 1; index >= 0; index -= 1) {
325
+ const layerId = group.stack[index];
326
+ if (layerId === exceptId) continue;
327
+ const task = _tasks[layerId];
328
+ if (task && (task.view === 2 || task.view === 3)) return task;
329
+ }
330
+ return undefined;
331
+ };
332
+
122
333
  const hasInlineProperty = (style: CSSStyleDeclaration, property: string) => {
123
334
  for (let index = 0; index < style.length; index += 1) {
124
335
  if (style.item(index) === property) return true;
@@ -307,25 +518,78 @@
307
518
 
308
519
  // 状态管理
309
520
  const state = reactive({
310
- view: 0,
521
+ coverDark: '' as string | number,
522
+ immediate: false,
523
+ openMode: 'cover' as LayerMode,
311
524
  params: {},
525
+ parkAfterHide: false,
526
+ recess: 0,
527
+ stackIndex: 0,
528
+ view: 0,
312
529
  });
313
530
  const scrollLockOwner = {};
314
531
 
532
+ const layerStyle = computed(() => {
533
+ let style = '';
534
+ if (`${props.dark}` !== '') style += `--layer-dark:${props.dark};`;
535
+ if (`${state.coverDark}` !== '') style += `--layer-cover-dark:${state.coverDark};`;
536
+ if (props.zIndex) style += `z-index:${props.zIndex};`;
537
+ else if (state.stackIndex > 0) style += `z-index:${50 + state.stackIndex};`;
538
+ return style;
539
+ });
540
+
541
+ const syncTaskView = (view: number) => {
542
+ if (!_tasks[props.id]) return;
543
+ _tasks[props.id].view = view;
544
+ _tasks[props.id].openMode = state.openMode;
545
+ _tasks[props.id].parkAfterHide = state.parkAfterHide;
546
+ _tasks[props.id].participates = participatesInGroup(props.dark);
547
+ _tasks[props.id].dark = props.dark;
548
+ };
549
+
550
+ const syncOwnDarkOccupancy = () => {
551
+ if (!env.client || !participatesInGroup(props.dark) || !props.id) return;
552
+ const group = getAreaGroup(props.area);
553
+ const immediate = state.immediate;
554
+ if (
555
+ occupiesGlobalDark({
556
+ dark: props.dark,
557
+ parkAfterHide: state.parkAfterHide,
558
+ participates: true,
559
+ view: state.view,
560
+ })
561
+ ) {
562
+ acquireGlobalDark(props.area, group, props.id, immediate);
563
+ } else {
564
+ releaseGlobalDark(props.area, group, props.id, immediate);
565
+ }
566
+ };
567
+
315
568
  watch(
316
569
  () => state.view,
317
570
  (view: any) => {
318
- if (_tasks[props.id]) {
319
- _tasks[props.id].view = view;
320
- }
571
+ syncTaskView(view);
321
572
  if (view === 2 || view === 3) pushEscapeStack(props.id);
322
573
  else removeEscapeStack(props.id);
323
574
  if (view > 0) acquireScrollLock(scrollLockOwner);
324
575
  else releaseScrollLock(scrollLockOwner);
576
+ syncOwnDarkOccupancy();
577
+ state.immediate = false;
578
+ if (participatesInGroup(props.dark) && props.id) {
579
+ nextTick(() => recomputeRecess(getAreaGroup(props.area)));
580
+ }
325
581
  },
326
582
  { deep: true, immediate: true },
327
583
  );
328
584
 
585
+ watch(
586
+ () => props.dark,
587
+ () => {
588
+ syncTaskView(state.view);
589
+ syncOwnDarkOccupancy();
590
+ },
591
+ );
592
+
329
593
  watch(
330
594
  () => props.escClose,
331
595
  (escClose) => {
@@ -350,6 +614,138 @@
350
614
  none: [],
351
615
  };
352
616
 
617
+ const resolveMode = (params: any): LayerMode => {
618
+ const mode = params?.mode;
619
+ if (mode === 'cascade' || mode === 'displace' || mode === 'replace' || mode === 'queue') return mode;
620
+ return 'cover';
621
+ };
622
+
623
+ const bindOpenCallbacks = (params: any = {}, then = () => {}) => {
624
+ if (params?.on?.show) on.show.push(params.on.show);
625
+ if (params?.on?.hide) on.hide.push(params.on.hide);
626
+ on.show.push(then);
627
+ };
628
+
629
+ const runCallbacks = (key: 'show' | 'hide' | 'none') => {
630
+ on[key].forEach((fun: any) => {
631
+ if (isFunction(fun)) fun();
632
+ });
633
+ on[key].length = 0;
634
+ };
635
+
636
+ const flushQueue = (group: AreaGroup) => {
637
+ if (groupHasBlocking(group) || !group.queue.length) return;
638
+ const job = group.queue.shift();
639
+ if (!job) return;
640
+ const task = _tasks[job.id];
641
+ if (!task) {
642
+ flushQueue(group);
643
+ return;
644
+ }
645
+ const playParams = { ...job.params };
646
+ if (playParams.mode === 'queue') delete playParams.mode;
647
+ if (job.kind === 'open') task.open(playParams, job.then);
648
+ else task.show(playParams, job.then);
649
+ };
650
+
651
+ const scheduleFlushQueue = (group: AreaGroup) => {
652
+ const flush = () => flushQueue(group);
653
+ if (group.dark.view === 1) {
654
+ const prev = group.dark.afterHide;
655
+ group.dark.afterHide = () => {
656
+ prev?.();
657
+ flush();
658
+ };
659
+ return;
660
+ }
661
+ nextTick(flush);
662
+ };
663
+
664
+ const onGroupLayerClosed = (layerId: string) => {
665
+ if (!participatesInGroup(props.dark)) return;
666
+ const group = getAreaGroup(props.area);
667
+ removeFromStack(group, layerId);
668
+ const topId = group.stack[group.stack.length - 1];
669
+ if (topId && _tasks[topId]?.view === 4) _tasks[topId].unpark();
670
+ recomputeRecess(group);
671
+ scheduleFlushQueue(group);
672
+ };
673
+
674
+ const closeReplacedLayers = (mode: LayerMode, params: any, immediate: boolean) => {
675
+ if (mode !== 'replace' || !participatesInGroup(props.dark) || !props.id) return;
676
+ const group = getAreaGroup(props.area);
677
+ const prev = getVisibleTop(group, props.id);
678
+ const targets = params?.replace === 'all' ? group.stack.filter((layerId) => layerId !== props.id) : prev ? [prev.id] : [];
679
+ targets.forEach((layerId) => {
680
+ const task = _tasks[layerId];
681
+ if (!task) return;
682
+ if (immediate) task.none();
683
+ else task.hide();
684
+ });
685
+ };
686
+
687
+ const applyIncomingRelation = (mode: LayerMode, params: any, immediate: boolean) => {
688
+ if (!participatesInGroup(props.dark) || !props.id) {
689
+ state.coverDark = '';
690
+ state.recess = 0;
691
+ return;
692
+ }
693
+ const group = getAreaGroup(props.area);
694
+ const prev = getVisibleTop(group, props.id);
695
+ state.coverDark = resolveCoverDark(mode, !!prev, params?.coverDark);
696
+ state.recess = 0;
697
+
698
+ if (mode === 'displace' && prev) prev.park({ immediate });
699
+ ensureOnStack(group, props.id);
700
+ };
701
+
702
+ const syncGroupPresentation = () => {
703
+ if (!participatesInGroup(props.dark) || !props.id) return;
704
+ recomputeRecess(getAreaGroup(props.area));
705
+ };
706
+
707
+ const tryEnqueue = (kind: 'open' | 'show', params: any, then: any) => {
708
+ if (!participatesInGroup(props.dark) || resolveMode(params) !== 'queue' || !props.id) return false;
709
+ const group = getAreaGroup(props.area);
710
+ if (!groupHasBlocking(group, props.id)) return false;
711
+ removeFromQueue(group, props.id);
712
+ group.queue.push({ id: props.id, kind, params, then });
713
+ return true;
714
+ };
715
+
716
+ const setRecess = (depth: number) => {
717
+ state.recess = depth;
718
+ };
719
+
720
+ const setStackIndex = (index: number) => {
721
+ state.stackIndex = index;
722
+ };
723
+
724
+ const park = (options: any = {}) => {
725
+ if (options.immediate) {
726
+ state.parkAfterHide = false;
727
+ state.immediate = true;
728
+ state.view = 4;
729
+ return;
730
+ }
731
+ if (state.view === 2 || state.view === 3) {
732
+ state.parkAfterHide = true;
733
+ syncTaskView(state.view);
734
+ state.view = 1;
735
+ }
736
+ };
737
+
738
+ const unpark = (options: any = {}) => {
739
+ if (state.view !== 4) return;
740
+ state.parkAfterHide = false;
741
+ if (options.immediate) {
742
+ state.immediate = true;
743
+ state.view = 3;
744
+ return;
745
+ }
746
+ state.view = 2;
747
+ };
748
+
353
749
  // 注入参数
354
750
  const init = (params: any = {}) => {
355
751
  _tasks[id].params = params;
@@ -364,68 +760,86 @@
364
760
 
365
761
  // 显示弹层
366
762
  const show = (params: any = {}, then = () => {}) => {
763
+ if (tryEnqueue('show', params, then)) return;
764
+ if (participatesInGroup(props.dark) && props.id) removeFromQueue(getAreaGroup(props.area), props.id);
765
+ bindOpenCallbacks(params, then);
766
+ state.openMode = resolveMode(params);
767
+ applyIncomingRelation(state.openMode, params, false);
768
+ state.parkAfterHide = false;
367
769
  state.view = 2;
368
- if (params?.on?.show) {
369
- on.show.push(params.on.show);
370
- }
371
- if (params?.on?.hide) {
372
- on.hide.push(params.on.hide);
373
- }
374
- on.show.push(then);
770
+ closeReplacedLayers(state.openMode, params, false);
771
+ syncGroupPresentation();
375
772
  };
376
773
 
377
774
  // 隐藏弹层
378
775
  const hide = (_params: any = {}, then = () => {}) => {
379
- if (state.view === 3) {
776
+ if (state.view === 4) {
777
+ on.hide.push(then);
778
+ state.parkAfterHide = false;
779
+ state.immediate = true;
780
+ state.view = 0;
781
+ runCallbacks('hide');
782
+ onGroupLayerClosed(id);
783
+ return;
784
+ }
785
+ if (state.view === 2 || state.view === 3) {
786
+ state.parkAfterHide = false;
787
+ syncTaskView(state.view);
380
788
  state.view = 1;
381
789
  on.hide.push(then);
382
790
  }
383
791
  };
384
792
 
385
793
  // 打开弹层(没有动画过程)
386
- const open = (_params: any = {}, then = () => {}) => {
794
+ const open = (params: any = {}, then = () => {}) => {
795
+ if (tryEnqueue('open', params, then)) return;
796
+ if (participatesInGroup(props.dark) && props.id) removeFromQueue(getAreaGroup(props.area), props.id);
797
+ bindOpenCallbacks(params, then);
798
+ state.openMode = resolveMode(params);
799
+ applyIncomingRelation(state.openMode, params, true);
800
+ state.parkAfterHide = false;
801
+ state.immediate = true;
387
802
  state.view = 3;
388
- on.show.push(then);
803
+ closeReplacedLayers(state.openMode, params, true);
804
+ syncGroupPresentation();
805
+ runCallbacks('show');
389
806
  };
390
807
  // 关闭弹层(没有动画过程)
391
808
  const none = (_params: any = {}, then = () => {}) => {
809
+ const wasOpen = state.view > 0;
810
+ if (participatesInGroup(props.dark) && props.id) removeFromQueue(getAreaGroup(props.area), props.id);
811
+ state.parkAfterHide = false;
812
+ state.immediate = true;
392
813
  state.view = 0;
393
814
  on.none.push(then);
394
815
  // 立即执行回调,因为没有动画
395
- on.none.forEach((fun: any) => {
396
- if (isFunction(fun)) {
397
- fun();
398
- }
399
- });
400
- on.none.length = 0;
816
+ runCallbacks('none');
817
+ if (wasOpen) onGroupLayerClosed(id);
401
818
  };
402
819
 
403
820
  // 点暗层关闭
404
821
  const _sys_hide = (ev: any) => {
405
822
  if (props.darkClickClose && ev.target.className === 'fekit-layer-wrap' && state.view === 3) {
406
- state.view = 1;
823
+ hide();
407
824
  }
408
825
  };
409
826
 
410
827
  // 监听动画
411
- const anim = () => {
828
+ const anim = (ev?: AnimationEvent) => {
829
+ if (ev && ev.target !== ev.currentTarget) return;
412
830
  if (state.view === 1) {
831
+ if (state.parkAfterHide) {
832
+ state.parkAfterHide = false;
833
+ state.view = 4;
834
+ return;
835
+ }
413
836
  state.view = 0;
414
- on.hide.forEach((fun: any) => {
415
- if (isFunction(fun)) {
416
- fun();
417
- }
418
- });
419
- on.hide.length = 0; // 清空回调数组
837
+ runCallbacks('hide');
838
+ onGroupLayerClosed(id);
420
839
  }
421
840
  if (state.view === 2) {
422
841
  state.view = 3;
423
- on.show.forEach((fun: any) => {
424
- if (isFunction(fun)) {
425
- fun();
426
- }
427
- });
428
- on.show.length = 0; // 清空回调数组
842
+ runCallbacks('show');
429
843
  }
430
844
  };
431
845
 
@@ -435,7 +849,25 @@
435
849
  console.error('Layer 有重复 id', id);
436
850
  }
437
851
  if (id) {
438
- _tasks[id] = { init, show, hide, open, none, view: state.view, escClose: props.escClose, darkClickClose: props.darkClickClose };
852
+ _tasks[id] = {
853
+ dark: props.dark,
854
+ darkClickClose: props.darkClickClose,
855
+ escClose: props.escClose,
856
+ hide,
857
+ id,
858
+ init,
859
+ none,
860
+ open,
861
+ openMode: state.openMode,
862
+ park,
863
+ parkAfterHide: state.parkAfterHide,
864
+ participates: participatesInGroup(props.dark),
865
+ setRecess,
866
+ setStackIndex,
867
+ show,
868
+ unpark,
869
+ view: state.view,
870
+ };
439
871
  ensureEscapeListener();
440
872
  } else {
441
873
  console.error('Layer 必须填写全局统一的 id');
@@ -443,6 +875,17 @@
443
875
 
444
876
  // 卸载弹层
445
877
  onBeforeUnmount(() => {
878
+ if (participatesInGroup(props.dark) && id) {
879
+ const group = getAreaGroup(props.area);
880
+ removeFromQueue(group, id);
881
+ if (state.view > 0) {
882
+ state.immediate = true;
883
+ state.view = 0;
884
+ onGroupLayerClosed(id);
885
+ } else {
886
+ removeFromStack(group, id);
887
+ }
888
+ }
446
889
  removeEscapeStack(id);
447
890
  releaseScrollLock(scrollLockOwner);
448
891
  delete _tasks[id];
@@ -453,24 +896,122 @@
453
896
  @use '@neatui/css' as neatui;
454
897
 
455
898
  // 弹层
899
+ @keyframes am-layer-dark-show {
900
+ 0% {
901
+ opacity: 0;
902
+ }
903
+ 100% {
904
+ opacity: 1;
905
+ }
906
+ }
907
+
908
+ @keyframes am-layer-dark-hide {
909
+ 0% {
910
+ opacity: 1;
911
+ }
912
+ 100% {
913
+ opacity: 0;
914
+ }
915
+ }
916
+
917
+ .fekit-layer-dark {
918
+ --layer-dark: 0.75;
919
+ position: fixed;
920
+ inset: 0;
921
+ z-index: 50;
922
+ pointer-events: none;
923
+ background-color: rgba(0, 0, 0, var(--layer-dark));
924
+ opacity: 0;
925
+ transition: background-color 180ms linear;
926
+
927
+ &[view='2'] {
928
+ animation: am-layer-dark-show 320ms cubic-bezier(0.4, 0, 0.2, 1) both;
929
+ }
930
+
931
+ &[view='3'] {
932
+ opacity: 1;
933
+ }
934
+
935
+ &[view='1'] {
936
+ animation: am-layer-dark-hide 260ms cubic-bezier(0.4, 0, 0.2, 1) both;
937
+ }
938
+
939
+ &[view='0'] {
940
+ opacity: 0;
941
+ }
942
+ }
943
+
456
944
  .fekit-layer {
945
+ --layer-dark: 0.75;
946
+ --layer-cover-dark: 0.32;
457
947
  position: fixed;
458
948
  width: 100%;
459
949
  height: 100%;
460
950
  left: 0;
461
951
  top: 0;
462
952
  z-index: 50;
463
- background-color: rgba(0, 0, 0, 0.75);
953
+ background-color: transparent;
464
954
  pointer-events: none;
465
955
 
956
+ .fekit-layer-cover-dark {
957
+ position: absolute;
958
+ inset: 0;
959
+ z-index: 0;
960
+ background-color: rgba(0, 0, 0, var(--layer-cover-dark));
961
+ opacity: 0;
962
+ pointer-events: none;
963
+ }
964
+
965
+ &[view='2'] .fekit-layer-cover-dark {
966
+ animation: am-layer-dark-show 320ms cubic-bezier(0.4, 0, 0.2, 1) both;
967
+ }
968
+
969
+ &[view='3'] .fekit-layer-cover-dark {
970
+ opacity: 1;
971
+ }
972
+
973
+ &[view='1'] .fekit-layer-cover-dark {
974
+ animation: am-layer-dark-hide 260ms cubic-bezier(0.4, 0, 0.2, 1) both;
975
+ }
976
+
977
+ &[view='4'] {
978
+ pointer-events: none;
979
+
980
+ .fekit-layer-wrap {
981
+ pointer-events: none;
982
+
983
+ & > * {
984
+ opacity: 0;
985
+ visibility: hidden;
986
+ }
987
+ }
988
+ }
989
+
990
+ &[view='3'][data-recess='1'] .fekit-layer-wrap > *:not(.fekit-layer-cover-dark) {
991
+ transform: scale(0.94);
992
+ opacity: 0.88;
993
+ transition:
994
+ transform 280ms cubic-bezier(0.4, 0, 0.2, 1),
995
+ opacity 280ms cubic-bezier(0.4, 0, 0.2, 1);
996
+ }
997
+
998
+ &[view='3'][data-recess='2'] .fekit-layer-wrap > *:not(.fekit-layer-cover-dark) {
999
+ transform: scale(0.88);
1000
+ opacity: 0.76;
1001
+ transition:
1002
+ transform 280ms cubic-bezier(0.4, 0, 0.2, 1),
1003
+ opacity 280ms cubic-bezier(0.4, 0, 0.2, 1);
1004
+ }
1005
+
466
1006
  &-wrap {
467
1007
  position: relative;
468
1008
  width: 100%;
469
1009
  height: 100%;
470
1010
  pointer-events: auto;
471
1011
 
472
- & > * {
1012
+ & > *:not(.fekit-layer-cover-dark) {
473
1013
  position: relative !important;
1014
+ z-index: 1;
474
1015
 
475
1016
  &.com-dialog {
476
1017
  max-width: 80%;
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : ''" class="mc-cascader">
2
+ <div :ui-form="`${theme} type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : ''" class="mc-cascader">
3
3
  <IFollowView
4
4
  ref="ifollow"
5
5
  :tipBoxStyle="`${state.flatOpts.length && state.flatOpts[0]?.length > 0 ? 'min-width: 10em;' : 'min-width: 100%'}`"
@@ -17,7 +17,7 @@
17
17
  <template v-if="multiple">
18
18
  <li v-for="(item, idx) of opt" :key="idx" class="ux-hover nx-sm r-ss nowrap" @click="multiSelect(item, optIndex)">
19
19
  <div ui-flex="row xm">
20
- <div ui-form="@a type:checkbox :block`">
20
+ <div :ui-form="`${theme} type:checkbox :block`">
21
21
  <input
22
22
  :checked="item.checkedStatus === 'all'"
23
23
  :class="`mr-sm ${item.checkedStatus === 'part' ? 'part-style' : ''}`"
@@ -71,7 +71,7 @@
71
71
  <span>单选</span>
72
72
  </p>
73
73
  </div>
74
- <label v-else class="nx-sm r-ss nowrap" ui-form="@a type:checkbox :block`">
74
+ <label v-else class="nx-sm r-ss nowrap" :ui-form="`${theme} type:checkbox :block`">
75
75
  <input :checked="state.checkAll" name="checkAll" type="checkbox" @change="checkAllChange" />
76
76
  <span :class="`${state.checkAll ? 'co-main' : ''}`">全选</span>
77
77
  </label>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select sz:${sz}`" ui-tips="@a co:well" :ui-tips-view="state.show">
2
+ <div :ui-form="`${theme} type:select sz:${sz}`" ui-tips="@a co:well" :ui-tips-view="state.show">
3
3
  <IFollowView ref="ifollow">
4
4
  <Input
5
5
  v-bind="{ placeholder, rules, theme, sz, co, block, ready, type, tips, maxlength, minlength, disabled, readonly, clearable, verify, prefix, suffix, interacted }"
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select sz:${sz}`" ui-tips="@a co:well" :ui-tips-view="state.show">
2
+ <div :ui-form="`${theme} type:select sz:${sz}`" ui-tips="@a co:well" :ui-tips-view="state.show">
3
3
  <IFollowView ref="ifollow">
4
4
  <InputRange
5
5
  v-bind="{ placeholder, rules, theme, sz, co, block, ready, type, tips, maxlength, minlength, disabled, readonly, clearable, verify, prefix, suffix, interacted }"
@@ -11,7 +11,7 @@
11
11
  </template>
12
12
  <template v-if="(!multiple && files && files.length === 0) || (multiple && (!limit || (limit && files && files.length < limit)))">
13
13
  <slot>
14
- <div ui-flex="row cm" ui-form="@a type:upload tips:hover" :class="`upload bg-fore b-solid b-case b-xs mb-sl ${uploadClass}`" :style="uploadStyle">
14
+ <div ui-flex="row cm" :ui-form="`${theme} type:upload tips:hover`" :class="`upload bg-fore b-solid b-case b-xs mb-sl ${uploadClass}`" :style="uploadStyle">
15
15
  <div v-if="tips" ui-form-tips>
16
16
  {{ tips }}
17
17
  </div>
@@ -52,6 +52,7 @@
52
52
  multiple?: boolean; // 是否可以上传多个文件
53
53
  crop?: boolean; // 是否需要抠图处理
54
54
  action?: string; // 上传路径
55
+ theme?: string;
55
56
  }
56
57
 
57
58
  const props = withDefaults(defineProps<Props>(), {
@@ -64,6 +65,7 @@
64
65
  multiple: false,
65
66
  crop: false,
66
67
  action: '/common/updateFile',
68
+ theme: '@a',
67
69
  });
68
70
 
69
71
  const emit = defineEmits(['update:modelValue', 'change']);
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select ${sz ? 'sz:' + sz : ''}`">
2
+ <div :ui-form="`${theme} type:select ${sz ? 'sz:' + sz : ''}`">
3
3
  <IFollowView ref="ifollow" tipBoxClass="ny-sm nx-no" tipBoxStyle="min-width: 100%">
4
4
  <slot>
5
5
  <Input
@@ -14,7 +14,7 @@
14
14
  </slot>
15
15
  <template #tips>
16
16
  <div v-if="search" class="ny-sm nx-sl">
17
- <Input v-model="state.search" sz="s">
17
+ <Input v-model="state.search" :theme="theme" sz="s">
18
18
  <template #suffix>
19
19
  <i ui-form-search="" class="co-note"></i>
20
20
  </template>
@@ -23,13 +23,13 @@
23
23
  <ul v-if="state.filter?.length" ui-scroll=":y s" class="nx-sm lh-ml" style="min-width: 100%; max-height: 15em">
24
24
  <template v-for="(item, idx) of state.filter" :key="idx">
25
25
  <li v-if="item.disabled" class="ux-hover nx-sm r-ss o-ms nowrap">
26
- <label style="pointer-events: none" ui-form="@a type:checkbox :block">
26
+ <label style="pointer-events: none" :ui-form="`${theme} type:checkbox :block`">
27
27
  <input type="checkbox" :name="state.name" :checked="state.processedValue?.some((i: any) => `${i}` === `${item.value}`)" />
28
28
  <del class="ml-sm">{{ item.label || item }}</del>
29
29
  </label>
30
30
  </li>
31
31
  <li v-else class="ux-hover nx-sm r-ss nowrap" @click.stop="select(item.value)">
32
- <label style="pointer-events: none" ui-form="@a type:checkbox :block">
32
+ <label style="pointer-events: none" :ui-form="`${theme} type:checkbox :block`">
33
33
  <input type="checkbox" :name="state.name" :checked="state.processedValue?.some((i: any) => `${i}` === `${item.value}`)" />
34
34
  <span class="ml-sm">{{ item.label || item }}</span>
35
35
  </label>
@@ -41,7 +41,7 @@
41
41
  <li class="my-ss"><div class="o-mm" ui-line="@a"></div></li>
42
42
  <li ui-flex="row xm">
43
43
  <div class="ux-hover nx-sm ny-ss r-ss nowrap co-main" @click.stop="allselect()">
44
- <label style="pointer-events: none" ui-form="@a type:checkbox :block">
44
+ <label style="pointer-events: none" :ui-form="`${theme} type:checkbox :block`">
45
45
  <input type="checkbox" :name="state.name" :checked="state.all" />
46
46
  <span class="ml-sm">全选({{ state.selectedCount }}/{{ state.availableCount }})</span>
47
47
  </label>
@@ -8,7 +8,7 @@
8
8
  class="n-sm r-ss b-solid b-xs"
9
9
  @click.stop="select(item.value)"
10
10
  >
11
- <label ui-form="@a type:checkbox :block" style="pointer-events: none">
11
+ <label :ui-form="`${theme} type:checkbox :block`" style="pointer-events: none">
12
12
  <input :checked="mv?.some((i: any) => `${i}` === `${item.value}`)" :name="field" type="checkbox" />
13
13
  <span class="ml-sm">{{ item.label || item }}</span>
14
14
  </label>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select ${sz ? 'sz:' + sz : ''}`">
2
+ <div :ui-form="`${theme} type:select ${sz ? 'sz:' + sz : ''}`">
3
3
  <IPickerView ref="ipicker" tipBoxClass="ny-sm nx-no" tipBoxStyle="min-width: 100%">
4
4
  <slot>
5
5
  <Input
@@ -16,13 +16,13 @@
16
16
  <ul v-if="props.options?.length" ui-scroll=":y s" class="nx-sm lh-ml" style="min-width: 100%; max-height: 15em">
17
17
  <template v-for="(item, idx) of props.options" :key="idx">
18
18
  <li v-if="item.disable" class="ux-hover nx-sm r-ss o-ms">
19
- <label style="pointer-events: none" ui-form="@a type:checkbox :block">
19
+ <label style="pointer-events: none" :ui-form="`${theme} type:checkbox :block`">
20
20
  <input type="checkbox" :name="state.name" :checked="modelValue?.some((i: any) => `${i}` === `${item.value}`)" />
21
21
  <del class="ml-sm">{{ item.label || item }}</del>
22
22
  </label>
23
23
  </li>
24
24
  <li v-else class="ux-hover nx-sm r-ss" @click.stop="select(item.value)">
25
- <label style="pointer-events: none" ui-form="@a type:checkbox :block">
25
+ <label style="pointer-events: none" :ui-form="`${theme} type:checkbox :block`">
26
26
  <input type="checkbox" :name="state.name" :checked="modelValue?.some((i: any) => `${i}` === `${item.value}`)" />
27
27
  <span class="ml-sm">{{ item.label || item }}</span>
28
28
  </label>
@@ -34,7 +34,7 @@
34
34
  <li class="my-ss"><div class="o-mm" ui-line="@a"></div></li>
35
35
  <li ui-flex="row xm">
36
36
  <div class="ux-hover nx-sm r-ss nowrap co-main" @click.stop="allselect()">
37
- <label style="pointer-events: none" ui-form="@a type:checkbox :block">
37
+ <label style="pointer-events: none" :ui-form="`${theme} type:checkbox :block`">
38
38
  <input type="checkbox" :name="state.name" :checked="state.all" />
39
39
  <span class="ml-sm">全选({{ props.options?.length || 0 }})</span>
40
40
  </label>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select sz:${sz}`" ref="dom">
2
+ <div :ui-form="`${theme} type:select sz:${sz}`" ref="dom">
3
3
  <IPickerView ref="ipicker">
4
4
  <Input v-bind="$props" :clearable="false" readonly v-model="state.value">
5
5
  <template #suffix><i ui-form-select=""></i></template>
@@ -14,7 +14,7 @@
14
14
  <IScrollView class="full n-ms" :pull="false" :load="false">
15
15
  <ul v-if="props.options?.length" class="my-sm-sub">
16
16
  <li class="ux-hover n-sm r-ss nowrap b-solid b-case b-xs" v-for="(item, idx) of props.options" :key="idx" @click.stop="select(item.value)">
17
- <label style="pointer-events: none" ui-form="@a type:checkbox :block">
17
+ <label style="pointer-events: none" :ui-form="`${theme} type:checkbox :block`">
18
18
  <input type="checkbox" :name="field" :checked="mv?.some((i: any) => `${i}` === `${item.value}`)" />
19
19
  <span class="ml-sm">{{ item.label || item }}</span>
20
20
  </label>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select ${sz ? 'sz:' + sz : ''}`" :ui-form-open="state.show ? 'show' : ''" ref="dom">
2
+ <div :ui-form="`${theme} type:select ${sz ? 'sz:' + sz : ''}`" :ui-form-open="state.show ? 'show' : ''" ref="dom">
3
3
  <IFollowView ref="ifollow" tipBoxClass="ny-sm nx-no" tipBoxStyle="min-width: 100%">
4
4
  <slot>
5
5
  <Input v-bind="$props" :clearable="false" readonly v-model="state.selectd">
@@ -8,7 +8,7 @@
8
8
  </slot>
9
9
  <template #tips>
10
10
  <div v-if="filterable" class="ny-sm nx-sl" style="height: 3em">
11
- <div :ui-form="`@a type:input sz:s :block`">
11
+ <div :ui-form="`${theme} type:input sz:s :block`">
12
12
  <input type="text" :placeholder="props.placeholder" v-model="state.search" />
13
13
  <div ui-form-suffix="">
14
14
  <i ui-form-clean="" v-if="state.search" @click.stop="state.search = ''"></i>
@@ -18,7 +18,7 @@
18
18
  </div>
19
19
  <ul v-if="props.options?.length" ui-form-dropdown="" ui-scroll=":y s" class="nx-sm lh-ml" style="min-width: 100%; max-height: 15em">
20
20
  <li class="ux-hover nx-sm r-ss nowrap" v-for="(item, idx) of props.options" :key="idx" @click.stop="select(item.value)">
21
- <label style="pointer-events: none" ui-form="@a type:checkbox :block">
21
+ <label style="pointer-events: none" :ui-form="`${theme} type:checkbox :block`">
22
22
  <input type="checkbox" :name="formName" :checked="modelValue?.some((i: any) => `${i}` === `${item.value}`)" />
23
23
  <span class="ml-sm">{{ item.label || item }}</span>
24
24
  </label>
@@ -29,7 +29,7 @@
29
29
  <li class="my-ss"><div class="o-mm" ui-line="@a"></div></li>
30
30
  <li ui-flex="row xm">
31
31
  <div class="ux-hover nx-sm r-ss nowrap co-main" @click.stop="allselect()">
32
- <label style="pointer-events: none" ui-form="@a type:checkbox :block">
32
+ <label style="pointer-events: none" :ui-form="`${theme} type:checkbox :block`">
33
33
  <input type="checkbox" :name="formName" :checked="state.all" />
34
34
  <!-- <span class="ml-sm">全选({{ props.options?.length || 0 }})</span> -->
35
35
  <span class="ml-sm">全选</span>
@@ -37,7 +37,7 @@
37
37
  </div>
38
38
  <div ui-flex="row cm" class="nx-sm">
39
39
  <button :ui-btn="`@a xs read :border :square ${state.pageInfo.page > 1 ? '' : ':disabled'}`" @click="prev"><i class="icon icon-prev"></i></button>
40
- <div :ui-form="`@a type:input sz:xs`" class="page-input mx-ss">
40
+ <div :ui-form="`${theme} type:input sz:xs`" class="page-input mx-ss">
41
41
  <input type="number" class="ac" :placeholder="props.placeholder" v-model="state.pageInfo.page" @change="pageChange" />
42
42
  </div>
43
43
  <button :ui-btn="`@a xs read :border :square ${state.pageInfo.page < pageInfo.pages ? '' : ':disabled'}`" @click="next"><i class="icon icon-next"></i></button>
@@ -1,12 +1,12 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : ''" ref="dom">
2
+ <div :ui-form="`${theme} type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : ''" ref="dom">
3
3
  <IFollowView ref="ifollow" tipBoxClass="ny-sm nx-no" tipBoxStyle="min-width: 100%">
4
4
  <Input v-bind="$props" :clearable="false" readonly v-model="state.selectd.label" :interacted="interacted">
5
5
  <template #suffix><i ui-form-select=""></i></template>
6
6
  </Input>
7
7
  <template #tips>
8
8
  <div v-if="filterable" class="ny-sm nx-sl" style="height: 3em">
9
- <div :ui-form="`@a type:input sz:s :block`">
9
+ <div :ui-form="`${theme} type:input sz:s :block`">
10
10
  <input type="text" :placeholder="props.placeholder" v-model="state.search" />
11
11
  <div ui-form-suffix="">
12
12
  <i ui-form-clean="" v-if="state.search" @click.stop="state.search = ''"></i>
@@ -25,7 +25,7 @@
25
25
  <li ui-flex="row xm">
26
26
  <div ui-flex="row cm" class="nx-sm">
27
27
  <button :ui-btn="`@a s read :border :square ${state.pageInfo.page > 1 ? '' : ':disabled'}`" @click="prev"><i class="icon icon-prev"></i></button>
28
- <div :ui-form="`@a type:input sz:s`" class="page-input mx-ss">
28
+ <div :ui-form="`${theme} type:input sz:s`" class="page-input mx-ss">
29
29
  <input type="number" class="ac" :placeholder="props.placeholder" v-model="state.pageInfo.page" @change="pageChange" />
30
30
  </div>
31
31
  <button :ui-btn="`@a s read :border :square ${state.pageInfo.page < pageInfo.pages ? '' : ':disabled'}`" @click="next"><i class="icon icon-next"></i></button>
@@ -1,12 +1,12 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : ''">
2
+ <div :ui-form="`${theme} type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : ''">
3
3
  <IFollowView ref="ifollow" tipBoxClass="ny-sm nx-no" tipBoxStyle="min-width: 100%" @onshow="onshow" @onhide="onhide">
4
4
  <Input v-bind="$props" :clearable="false" readonly v-model="state.selectd" @click="toggle" :interacted="interacted">
5
5
  <template #suffix><i ui-form-select=""></i></template>
6
6
  </Input>
7
7
  <template #tips>
8
8
  <div class="ny-sm nx-sl" style="height: 3em">
9
- <div :ui-form="`@a type:input sz:s :block`">
9
+ <div :ui-form="`${theme} type:input sz:s :block`">
10
10
  <input type="text" :placeholder="props.placeholder" v-model="state.search" />
11
11
  <div ui-form-suffix="">
12
12
  <i ui-form-clean="" v-if="state.search" @click.stop="state.search = ''"></i>
@@ -16,7 +16,7 @@
16
16
  </div>
17
17
  <ul v-if="props.options?.length" ui-form-dropdown="" ui-scroll=":y s" class="nx-sm lh-ml" style="min-width: 100%; max-height: 15em">
18
18
  <li class="ux-hover nx-sm r-ss nowrap" v-for="(item, idx) of state.filter" :key="idx" @click.stop="select(item.value)">
19
- <label style="pointer-events: none" ui-form="@a type:checkbox :block">
19
+ <label style="pointer-events: none" :ui-form="`${theme} type:checkbox :block`">
20
20
  <input type="checkbox" :name="formName" :checked="modelValue?.some((i: any) => `${i}` === `${item.value}`)" />
21
21
  <span class="ml-sm">{{ item.label || item }}</span>
22
22
  </label>
@@ -27,7 +27,7 @@
27
27
  <li class="my-ss"><div class="o-mm" ui-line="@a"></div></li>
28
28
  <li ui-flex="row xm">
29
29
  <div class="ux-hover nx-sm r-ss nowrap co-main" @click.stop="allselect()">
30
- <label style="pointer-events: none" ui-form="@a type:checkbox :block">
30
+ <label style="pointer-events: none" :ui-form="`${theme} type:checkbox :block`">
31
31
  <input type="checkbox" :name="formName" :checked="state.all" />
32
32
  <span class="ml-sm">全选({{ state.filter?.length || 0 }})</span>
33
33
  </label>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : ''">
2
+ <div :ui-form="`${theme} type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : ''">
3
3
  <IFollowView ref="ifollow" tipBoxClass="ny-sm nx-no" tipBoxStyle="min-width: 100%" @onshow="onshow" @onhide="onhide">
4
4
  <Input
5
5
  v-bind="{ placeholder, rules, theme, sz, co, block, ready, type, tips, disabled, readonly: true, clearable: false, verify, prefix, suffix, interacted }"
@@ -10,7 +10,7 @@
10
10
  </Input>
11
11
  <template #tips v-if="!readonly || !disabled">
12
12
  <div v-if="search" class="ny-sm nx-sl">
13
- <Input v-model="state.search" sz="s">
13
+ <Input v-model="state.search" :theme="theme" sz="s">
14
14
  <template #suffix>
15
15
  <i ui-form-search="" class="co-note"></i>
16
16
  </template>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : ''">
2
+ <div :ui-form="`${theme} type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : ''">
3
3
  <IFollowView ref="ifollow" tipBoxClass="ny-sm nx-no" tipBoxStyle="min-width: 100%" @onshow="onshow" @onhide="onhide">
4
4
  <Input
5
5
  v-bind="{ placeholder, rules, theme, sz, co, block, ready, type, tips, disabled, readonly: true, clearable: false, verify, prefix, suffix, interacted }"
@@ -13,7 +13,7 @@
13
13
  </Input>
14
14
  <template #tips v-if="!readonly && !disabled">
15
15
  <div v-if="search" class="ny-sm nx-sl">
16
- <Input v-model="state.search" :sz="sz">
16
+ <Input v-model="state.search" :theme="theme" :sz="sz">
17
17
  <template #suffix>
18
18
  <i ui-form-search="" class="co-note"></i>
19
19
  </template>
@@ -38,6 +38,7 @@
38
38
  v-model="customInputValue"
39
39
  v-bind="other.config || {}"
40
40
  :placeholder="other.config?.placeholder || '请输入自定义内容'"
41
+ :theme="theme"
41
42
  sz="s"
42
43
  @keyup.enter="confirmCustomInput"
43
44
  @blur="confirmCustomInput"
@@ -7,7 +7,7 @@
7
7
  :key="idx"
8
8
  @click="mv = item.value === mv ? '' : item.value"
9
9
  >
10
- <label ui-form="@a type:radio" style="pointer-events: none">
10
+ <label :ui-form="`${theme} type:radio`" style="pointer-events: none">
11
11
  <input type="radio" :name="field" :data-name="field" :checked="item.value === mv" /><span class="lh-ss">{{ item.label || item }}</span>
12
12
  </label>
13
13
  </li>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select sz:${sz}`" ref="dom">
2
+ <div :ui-form="`${theme} type:select sz:${sz}`" ref="dom">
3
3
  <IPickerView ref="ipicker">
4
4
  <Input v-bind="$props" :clearable="false" readonly v-model="state.selectd.label">
5
5
  <template #suffix><i ui-form-select=""></i></template>
@@ -14,7 +14,7 @@
14
14
  <IScrollView class="full n-ms" :pull="false" :load="false">
15
15
  <ul v-if="props.options?.length" class="my-sm-sub">
16
16
  <li class="ux-hover n-sm r-ss b-solid b-case b-xs" v-for="(item, idx) of props.options" :key="idx" @click="mv = item.value">
17
- <label ui-form="@a type:radio">
17
+ <label :ui-form="`${theme} type:radio`">
18
18
  <input type="radio" :name="item.field" :checked="item.value === mv" /><span>{{ item.label || item }}</span>
19
19
  </label>
20
20
  </li>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :ui-form="`@a type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : 'hide'">
2
+ <div :ui-form="`${theme} type:select sz:${sz}`" :ui-form-open="state.show ? 'show' : 'hide'">
3
3
  <IFollowView ref="ifollow" v-model="state.show" tipBoxClass="ny-sm nx-no" tipBoxStyle="min-width: 100%" @onshow="onshow" @onhide="onhide">
4
4
  <Input
5
5
  v-bind="{ placeholder, rules, theme, sz, co, block, ready, type, tips, disabled, readonly: true, clearable: false, verify, prefix, suffix, interacted }"
@@ -10,7 +10,7 @@
10
10
  </Input>
11
11
  <template #tips v-if="!readonly || !disabled">
12
12
  <div v-if="search" class="nb-sm nx-sl">
13
- <Input v-model="state.search" sz="s">
13
+ <Input v-model="state.search" :theme="theme" sz="s">
14
14
  <template #suffix>
15
15
  <i ui-form-search="" class="co-note"></i>
16
16
  </template>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <label ui-form="@a type:switch">
2
+ <label :ui-form="`${theme} type:switch`">
3
3
  <input type="checkbox" v-model="internalValue" :disabled="disabled" />
4
4
  <slot>
5
5
  <span>{{ text }}</span>
@@ -164,7 +164,7 @@
164
164
  <label
165
165
  class="pr flex-block"
166
166
  :class="{ 'ux-disable': disabled }"
167
- ui-form="@a type:upload tips:hover"
167
+ :ui-form="`${theme} type:upload tips:hover`"
168
168
  :style="`width: ${isNumber(width) ? width + 'em' : width}`"
169
169
  v-bind="itemAttrs"
170
170
  >
@@ -209,6 +209,7 @@
209
209
  disabled?: boolean;
210
210
  width?: number | string;
211
211
  ratio?: number;
212
+ theme?: string;
212
213
  }
213
214
 
214
215
  const props = withDefaults(defineProps<Props>(), {
@@ -226,6 +227,7 @@
226
227
  disabled: false,
227
228
  width: 10,
228
229
  ratio: 100,
230
+ theme: '@a',
229
231
  });
230
232
 
231
233
  interface UploadFile {