@vue-ui-kit/ant 2.4.1 → 2.4.3

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.
@@ -5,16 +5,36 @@
5
5
  setup
6
6
  >
7
7
  import { ColumnProps, PFormItemProps, PGridProps, ResponsePathConfig } from '#/antProxy';
8
+ import PFormCol from '@/components/PFormCol.vue';
9
+ import RenderDefaultSlots from '@/components/RenderDefaultSlots';
10
+ import RenderTitleSlots from '@/components/RenderTitleSlots';
11
+ import { $confirm } from '@/hooks/useMessage';
12
+ import Icon from '@/renders/Icon';
13
+ import { getGridDefaults } from '@/utils/config';
14
+ import { cleanCol, defaultLabelCol } from '@/utils/core';
15
+ import { isGoodValue } from '@/utils/is';
16
+ import { eachTree } from '@/utils/treeHelper';
17
+ import { DownOutlined } from '@ant-design/icons-vue';
18
+ import {
19
+ message as $message,
20
+ Button as AButton,
21
+ Form as AForm,
22
+ Row as ARow,
23
+ Spin as ASpin,
24
+ Table as ATable,
25
+ } from 'ant-design-vue';
26
+ import { v4 as uuid_v4 } from 'uuid';
8
27
  import {
9
- computed,
10
- useAttrs,
11
- ref,
12
28
  Ref,
13
- reactive,
29
+ computed,
30
+ nextTick,
31
+ onBeforeUnmount,
14
32
  onMounted,
15
- watch,
33
+ reactive,
34
+ ref,
16
35
  toRefs,
17
- onBeforeUnmount,
36
+ useAttrs,
37
+ watch,
18
38
  } from 'vue';
19
39
  import {
20
40
  debounce,
@@ -27,33 +47,13 @@
27
47
  merge,
28
48
  omit,
29
49
  } from 'xe-utils';
30
- import { eachTree } from '@/utils/treeHelper';
31
- import { message as $message } from 'ant-design-vue';
32
- import RenderTitleSlots from '@/components/RenderTitleSlots';
33
- import RenderDefaultSlots from '@/components/RenderDefaultSlots';
34
- import { v4 as uuid_v4 } from 'uuid';
35
- import { isGoodValue } from '@/utils/is';
36
- import PFormCol from '@/components/PFormCol.vue';
37
- import { cleanCol, defaultLabelCol } from '@/utils/core';
38
- import { getGridDefaults } from '@/utils/config';
39
- import Icon from '@/renders/Icon';
40
- import { $confirm } from '@/hooks/useMessage';
41
- import {
42
- Table as ATable,
43
- Button as AButton,
44
- Form as AForm,
45
- Row as ARow,
46
- Spin as ASpin,
47
- } from 'ant-design-vue';
48
- import { TablePaginationConfig } from 'ant-design-vue/es/table/interface';
49
- import { DownOutlined } from '@ant-design/icons-vue';
50
50
 
51
51
  const props = withDefaults(defineProps<PGridProps<D, F>>(), {
52
52
  rowKey: 'id',
53
53
  scrollMode: 'inner',
54
54
  align: () => getGridDefaults().align ?? 'left',
55
55
  lazyReset: () => getGridDefaults().lazyReset ?? false,
56
- fitHeight: () => getGridDefaults().fitHeight ?? 170,
56
+ fitHeight: () => getGridDefaults().fitHeight ?? 30,
57
57
  striped: () => getGridDefaults().striped ?? false,
58
58
  });
59
59
 
@@ -86,6 +86,7 @@
86
86
  const boxEl = ref<HTMLDivElement>();
87
87
  const pFormWrapper = ref<HTMLDivElement>();
88
88
  const tableWrapperEl = ref<HTMLDivElement>();
89
+ const tableFooterEl = ref<HTMLDivElement>();
89
90
  const renderHeight = ref(500);
90
91
  const selectedRowKeys = ref<Array<string | number>>([]);
91
92
  const selectedCaches = ref<D[]>([]);
@@ -184,6 +185,8 @@
184
185
  : 'list'
185
186
  : 'bad',
186
187
  );
188
+ /** 底部展示「已选」统计(与 multiple 无关,仅看 showCount) */
189
+ const showSelectionCount = computed(() => !!selectConfig.value?.showCount);
187
190
  const attrs = useAttrs();
188
191
  const emit = defineEmits<{
189
192
  (
@@ -286,7 +289,6 @@
286
289
  : {
287
290
  list: response,
288
291
  };
289
- const enoughSpacing = ref(true);
290
292
  const reload = () => {
291
293
  selectedCaches.value = [];
292
294
  selectedRowKeys.value = [];
@@ -298,15 +300,9 @@
298
300
  selectedRowKeys.value = [];
299
301
  return fetchData();
300
302
  };
301
- /**
302
- * @description
303
- * @param p
304
- * @param _filters todo filters
305
- * @param _sorter todo sorter
306
- */
307
- const handleTableChange = (p: TablePaginationConfig, _filters, _sorter) => {
308
- pagination.page = p.current!;
309
- pagination.size = p.pageSize!;
303
+ const handlePaginationChange = (page: number, pageSize: number) => {
304
+ pagination.page = page;
305
+ pagination.size = pageSize;
310
306
  return fetchData();
311
307
  };
312
308
 
@@ -363,18 +359,6 @@
363
359
  pagination.page = 1;
364
360
  return lazy ? Promise.resolve() : debounceFetchData();
365
361
  };
366
- const pg = computed(() =>
367
- mode.value === 'pagination'
368
- ? {
369
- current: pagination.page,
370
- total: totalCount.value,
371
- pageSize: pagination.size,
372
- responsive: false,
373
- showSizeChanger: true,
374
- showTotal: (total: number) => `共${total}条数据`,
375
- }
376
- : false,
377
- );
378
362
  const defaultTableConfig = {
379
363
  size: 'small',
380
364
  sticky: true,
@@ -423,16 +407,37 @@
423
407
  },
424
408
  { deep: true },
425
409
  );
410
+ watch(
411
+ () => [renderTableKey.value, dataSeed.value, mode.value, showSelectionCount.value],
412
+ () => nextTick(() => resizeTable()),
413
+ { flush: 'post' },
414
+ );
426
415
  let resizeRaf: number | null = null;
416
+
417
+ const resolveTableHeaderEl = (wrapper: HTMLElement): HTMLElement | null => {
418
+ const root = wrapper.querySelector('.ant-table-wrapper');
419
+ if (!root) return null;
420
+ const byHeader = root.querySelector('.ant-table-header') as HTMLElement | null;
421
+ if (byHeader) return byHeader;
422
+ return root.querySelector('.ant-table-thead') as HTMLElement | null;
423
+ };
424
+
427
425
  const resizeTable = () => {
428
426
  if (resizeRaf) cancelAnimationFrame(resizeRaf);
429
427
  resizeRaf = requestAnimationFrame(() => {
430
428
  resizeRaf = null;
431
429
  if (!tableWrapperEl.value) return;
432
- const rect = tableWrapperEl.value.getBoundingClientRect();
433
- renderHeight.value =
434
- props.renderY ?? Math.max(window.innerHeight - rect.top - props.fitHeight, 100);
435
- enoughSpacing.value = window.innerHeight > 600;
430
+ if (isGoodValue(props.renderY)) {
431
+ renderHeight.value = props.renderY as number;
432
+ return;
433
+ }
434
+ const wrapperH = tableWrapperEl.value.clientHeight;
435
+ const footerH = tableFooterEl.value?.offsetHeight ?? 0;
436
+ const tableHeaderEl = resolveTableHeaderEl(tableWrapperEl.value);
437
+ const headerH = tableHeaderEl?.getBoundingClientRect().height ?? 0;
438
+ const reserve = props.fitHeight ?? 30;
439
+ const y = Math.max(wrapperH - footerH - headerH - reserve, 100);
440
+ renderHeight.value = y;
436
441
  });
437
442
  };
438
443
 
@@ -469,6 +474,8 @@
469
474
 
470
475
  let observer: MutationObserver;
471
476
  let boxElResizeObserver: ResizeObserver | null = null;
477
+ let tableWrapperResizeObserver: ResizeObserver | null = null;
478
+ let tableFooterResizeObserver: ResizeObserver | null = null;
472
479
  onMounted(() => {
473
480
  resizeTable();
474
481
  window.addEventListener('resize', resizeTable);
@@ -495,7 +502,16 @@
495
502
  ancestor = ancestor.parentElement;
496
503
  }
497
504
  }
505
+ tableWrapperResizeObserver = new ResizeObserver(() => resizeTable());
506
+ if (tableWrapperEl.value) {
507
+ tableWrapperResizeObserver.observe(tableWrapperEl.value);
508
+ }
509
+ tableFooterResizeObserver = new ResizeObserver(() => resizeTable());
510
+ if (tableFooterEl.value) {
511
+ tableFooterResizeObserver.observe(tableFooterEl.value);
512
+ }
498
513
  resetQueryFormData(props.manualFetch);
514
+ nextTick(() => resizeTable());
499
515
  });
500
516
  const renderContent = (content: string | (() => any)) => {
501
517
  if (isFunction(content)) {
@@ -524,6 +540,8 @@
524
540
  observer.disconnect();
525
541
  formWrapperResizeObserver?.disconnect();
526
542
  boxElResizeObserver?.disconnect();
543
+ tableWrapperResizeObserver?.disconnect();
544
+ tableFooterResizeObserver?.disconnect();
527
545
  });
528
546
  </script>
529
547
  <template>
@@ -640,51 +658,72 @@
640
658
  </div>
641
659
  <div
642
660
  ref="tableWrapperEl"
643
- :class="`p-pane flex-1 ${enoughSpacing ? 'h-0' : ''} p-${scrollMode ?? 'inner'}-scroll`"
661
+ :class="`p-pane flex-1 h-0 min-h-0 flex flex-col p-${scrollMode ?? 'inner'}-scroll`"
644
662
  >
645
- <div
646
- v-if="selectConfig?.multiple && selectConfig.showCount"
647
- class="w-full text-slate-5 pl-4"
648
- >
649
- 已选:{{ selectedRowKeys.length }}
663
+ <div class="p-grid-table-body flex-1 min-h-0 overflow-hidden">
664
+ <a-table
665
+ :key="renderTableKey + '_table'"
666
+ :row-key="rowKey ?? 'id'"
667
+ ref="tableEl"
668
+ :row-class-name="
669
+ striped ? (_record, index) => (index % 2 === 1 ? 'p-grid-row-striped' : '') : ''
670
+ "
671
+ :columns="passDefaultColumnProps(columns ?? []).map((c) => cleanCol(c as ColumnProps))"
672
+ :data-source="tableData"
673
+ :loading="loading.table"
674
+ :pagination="false"
675
+ v-bind="tc"
676
+ :scroll="{
677
+ x: 'max-content',
678
+ y: renderHeight,
679
+ }"
680
+ >
681
+ <template v-if="slotTitleColumns.length > 0" #headerCell="{ column }">
682
+ <render-title-slots
683
+ v-if="slotTitleColumns.some((s) => column.key && s.field === column.key)"
684
+ :key="renderTableKey + '_title_' + dataSeed + '_slot_' + column.key"
685
+ :column="slotTitleColumns.find((f) => column.key && f.field === column.key)!"
686
+ />
687
+ </template>
688
+ <template v-if="slotDefaultColumns.length > 0" #bodyCell="{ column, record, index }">
689
+ <render-default-slots
690
+ v-if="slotDefaultColumns.some((s) => column.key && s.field === column.key)"
691
+ :key="renderTableKey + '_cell_' + dataSeed + '_slot_' + column.key"
692
+ :column="slotDefaultColumns.find((f) => column.key && f.field === column.key)!"
693
+ :default-handler="{ pick: pickRow, setLoadings }"
694
+ :row="record"
695
+ :row-index="index"
696
+ :table-data="tableData as Recordable[]"
697
+ />
698
+ </template>
699
+ </a-table>
650
700
  </div>
651
- <a-table
652
- :key="renderTableKey + '_table'"
653
- :row-key="rowKey ?? 'id'"
654
- ref="tableEl"
655
- :row-class-name="
656
- striped ? (_record, index) => (index % 2 === 1 ? 'p-grid-row-striped' : '') : ''
657
- "
658
- :columns="passDefaultColumnProps(columns ?? []).map((c) => cleanCol(c as ColumnProps))"
659
- :data-source="tableData"
660
- :loading="loading.table"
661
- :pagination="pg"
662
- v-bind="tc"
663
- :scroll="{
664
- x: 'max-content',
665
- y: renderHeight,
666
- }"
667
- @change="handleTableChange"
668
- >
669
- <template v-if="slotTitleColumns.length > 0" #headerCell="{ column }">
670
- <render-title-slots
671
- v-if="slotTitleColumns.some((s) => column.key && s.field === column.key)"
672
- :key="renderTableKey + '_title_' + dataSeed + '_slot_' + column.key"
673
- :column="slotTitleColumns.find((f) => column.key && f.field === column.key)!"
674
- />
675
- </template>
676
- <template v-if="slotDefaultColumns.length > 0" #bodyCell="{ column, record, index }">
677
- <render-default-slots
678
- v-if="slotDefaultColumns.some((s) => column.key && s.field === column.key)"
679
- :key="renderTableKey + '_cell_' + dataSeed + '_slot_' + column.key"
680
- :column="slotDefaultColumns.find((f) => column.key && f.field === column.key)!"
681
- :default-handler="{ pick: pickRow, setLoadings }"
682
- :row="record"
683
- :row-index="index"
684
- :table-data="tableData as Recordable[]"
701
+ <div ref="tableFooterEl" class="flex-shrink-0">
702
+ <div
703
+ v-if="mode !== 'pagination' && showSelectionCount"
704
+ class="w-full pt-8px px-16px text-slate-5"
705
+ >
706
+ 已选:{{ selectedRowKeys.length }}
707
+ </div>
708
+ <div
709
+ v-else-if="mode === 'pagination'"
710
+ class="flex w-full items-center gap-12px pt-8px px-16px"
711
+ >
712
+ <div class="flex-1 min-w-0 text-slate-5 flex items-center">
713
+ <template v-if="showSelectionCount"> 已选:{{ selectedRowKeys.length }} </template>
714
+ </div>
715
+ <a-pagination
716
+ size="small"
717
+ :responsive="false"
718
+ :show-size-changer="true"
719
+ :show-total="(total: number) => `共${total}条数据`"
720
+ :current="pagination.page"
721
+ :page-size="pagination.size"
722
+ :total="totalCount"
723
+ @change="handlePaginationChange"
685
724
  />
686
- </template>
687
- </a-table>
725
+ </div>
726
+ </div>
688
727
  </div>
689
728
  </template>
690
729
  </div>
@@ -1,29 +1,29 @@
1
- import { defineComponent } from 'vue';
2
- import { CellRender, RenderTableParams } from '#/antProxy';
3
- import renderStore from '@/store/renderStore';
4
-
5
- export default defineComponent(
6
- <D extends Recordable = Recordable>(props: {
7
- cellRender: CellRender;
8
- renderTableParams: RenderTableParams<D>;
9
- defaultHandler: {
10
- [key: string]: (...args: any[]) => any;
11
- };
12
- }) => {
13
- const { cellRender, renderTableParams } = props;
14
- const { data, row, field, rowIndex } = renderTableParams!;
15
- return () => {
16
- return (
17
- renderStore.renders[cellRender!.name]?.renderDefault?.(
18
- cellRender!,
19
- { data, row, field, rowIndex },
20
- props.defaultHandler,
21
- ) ?? null
22
- );
23
- };
24
- },
25
- {
26
- name: 'RenderAntCell',
27
- props: ['cellRender', 'renderTableParams', 'defaultHandler'],
28
- },
29
- );
1
+ import { defineComponent } from 'vue';
2
+ import { CellRender, RenderTableParams } from '#/antProxy';
3
+ import renderStore from '@/store/renderStore';
4
+
5
+ export default defineComponent(
6
+ <D extends Recordable = Recordable>(props: {
7
+ cellRender: CellRender;
8
+ renderTableParams: RenderTableParams<D>;
9
+ defaultHandler?: {
10
+ [key: string]: (...args: any[]) => any;
11
+ };
12
+ }) => {
13
+ const { cellRender, renderTableParams } = props;
14
+ const { data, row, field, rowIndex } = renderTableParams!;
15
+ return () => {
16
+ return (
17
+ renderStore.renders[cellRender!.name]?.renderDefault?.(
18
+ cellRender!,
19
+ { data, row, field, rowIndex },
20
+ props.defaultHandler,
21
+ ) ?? null
22
+ );
23
+ };
24
+ },
25
+ {
26
+ name: 'RenderAntCell',
27
+ props: ['cellRender', 'renderTableParams', 'defaultHandler'],
28
+ },
29
+ );
@@ -67,6 +67,22 @@
67
67
  gap: 8px;
68
68
  }
69
69
 
70
+ .gap-12px {
71
+ gap: 12px;
72
+ }
73
+
74
+ .flex-shrink-0 {
75
+ flex-shrink: 0;
76
+ }
77
+
78
+ .min-w-0 {
79
+ min-width: 0;
80
+ }
81
+
82
+ .min-h-0 {
83
+ min-height: 0;
84
+ }
85
+
70
86
  .pt-8px {
71
87
  padding-top: 8px;
72
88
  }
@@ -88,6 +104,10 @@
88
104
  overflow-y: auto;
89
105
  }
90
106
 
107
+ .overflow-hidden {
108
+ overflow: hidden;
109
+ }
110
+
91
111
  /* #endregion */
92
112
 
93
113
  /* #region P family styles */
@@ -273,7 +293,8 @@
273
293
  }
274
294
  }
275
295
  .p-inner-scroll {
276
- & > .ant-table-wrapper {
296
+ & > .ant-table-wrapper,
297
+ & > .p-grid-table-body > .ant-table-wrapper {
277
298
  height: 100%;
278
299
 
279
300
  & > .ant-spin-nested-loading {
@@ -1,92 +1,105 @@
1
- import { PFormProps, PGridProps } from '#/antProxy';
2
- import { ConfigType } from 'e-virt-table';
3
- import { clone } from 'xe-utils';
4
-
5
- // 全局配置接口
6
- export interface UIKitConfig {
7
- form?: {
8
- labelCol?: any;
9
- wrapperCol?: any;
10
- };
11
- grid?: {
12
- align?: 'left' | 'right' | 'center';
13
- lazyReset?: boolean;
14
- fitHeight?: number;
15
- fitCanvasHeight?: number;
16
- striped?: boolean;
17
- };
18
- canvasTable?: ConfigType;
19
- }
20
-
21
- // 默认配置
22
- const defaultConfig: UIKitConfig = {
23
- form: {
24
- labelCol: { span: 6 },
25
- wrapperCol: { span: 16 },
26
- },
27
- grid: {
28
- align: 'left',
29
- lazyReset: false,
30
- fitHeight: 170,
31
- striped: false,
32
- },
33
- canvasTable: {
34
- DISABLED: true,
35
- ENABLE_FINDER: true,
36
- AUTO_ROW_HEIGHT: true,
37
- },
38
- };
39
-
40
- // 当前配置(可被修改)
41
- let currentConfig: UIKitConfig = clone(defaultConfig, true);
42
-
43
- // 设置配置
44
- export function setUIKitConfig(config: Partial<UIKitConfig>): void {
45
- currentConfig = {
46
- form: {
47
- ...currentConfig.form,
48
- ...config.form,
49
- },
50
- grid: {
51
- ...currentConfig.grid,
52
- ...config.grid,
53
- },
54
- canvasTable: {
55
- ...currentConfig.canvasTable,
56
- ...config.canvasTable,
57
- },
58
- };
59
- }
60
-
61
- // 获取配置
62
- export function getUIKitConfig(): UIKitConfig {
63
- return currentConfig;
64
- }
65
-
66
- // 获取表单默认配置
67
- export function getFormDefaults(): Partial<PFormProps> {
68
- return {
69
- labelCol: currentConfig.form?.labelCol,
70
- wrapperCol: currentConfig.form?.wrapperCol,
71
- };
72
- }
73
-
74
- // 获取Grid默认配置
75
- export function getGridDefaults(): Partial<PGridProps> {
76
- return {
77
- align: currentConfig.grid?.align,
78
- lazyReset: currentConfig.grid?.lazyReset,
79
- fitHeight: currentConfig.grid?.fitHeight,
80
- striped: currentConfig.grid?.striped,
81
- };
82
- }
83
-
84
- // 获取CanvasTable默认配置
85
- export function getCanvasTableDefaults(): ConfigType {
86
- return currentConfig.canvasTable || ({} as ConfigType);
87
- }
88
-
89
- // 重置为默认配置
90
- export function resetUIKitConfig(): void {
91
- currentConfig = JSON.parse(JSON.stringify(defaultConfig));
92
- }
1
+ import { PFormProps, PGridProps } from '#/antProxy';
2
+ import { ConfigType } from 'e-virt-table';
3
+ import { clone } from 'xe-utils';
4
+ import type { VNode } from 'vue';
5
+
6
+ // 全局配置接口
7
+ export interface UIKitConfig {
8
+ form?: {
9
+ labelCol?: any;
10
+ wrapperCol?: any;
11
+ };
12
+ grid?: {
13
+ align?: 'left' | 'right' | 'center';
14
+ lazyReset?: boolean;
15
+ fitHeight?: number;
16
+ fitCanvasHeight?: number;
17
+ striped?: boolean;
18
+ };
19
+ canvasTable?: ConfigType;
20
+ /**
21
+ * 自定义 tooltip 渲染函数,替换全局所有 a-tooltip。
22
+ * @param defaultSlot tooltip 触发元素(图标等)
23
+ * @param content tooltip 内容,字符串或返回 VNode 的函数
24
+ */
25
+ renderTooltip?: (defaultSlot: () => VNode, content: string | (() => VNode)) => VNode;
26
+ }
27
+
28
+ // 默认配置
29
+ const defaultConfig: UIKitConfig = {
30
+ form: {
31
+ labelCol: { span: 6 },
32
+ wrapperCol: { span: 16 },
33
+ },
34
+ grid: {
35
+ align: 'left',
36
+ lazyReset: false,
37
+ fitHeight: 30,
38
+ striped: false,
39
+ },
40
+ canvasTable: {
41
+ DISABLED: true,
42
+ ENABLE_FINDER: true,
43
+ AUTO_ROW_HEIGHT: true,
44
+ },
45
+ };
46
+
47
+ // 当前配置(可被修改)
48
+ let currentConfig: UIKitConfig = clone(defaultConfig, true);
49
+
50
+ // 设置配置
51
+ export function setUIKitConfig(config: Partial<UIKitConfig>): void {
52
+ currentConfig = {
53
+ form: {
54
+ ...currentConfig.form,
55
+ ...config.form,
56
+ },
57
+ grid: {
58
+ ...currentConfig.grid,
59
+ ...config.grid,
60
+ },
61
+ canvasTable: {
62
+ ...currentConfig.canvasTable,
63
+ ...config.canvasTable,
64
+ },
65
+ renderTooltip: config.renderTooltip ?? currentConfig.renderTooltip,
66
+ };
67
+ }
68
+
69
+ // 获取自定义 tooltip 渲染函数
70
+ export function getTooltipRenderer(): UIKitConfig['renderTooltip'] {
71
+ return currentConfig.renderTooltip;
72
+ }
73
+
74
+ // 获取配置
75
+ export function getUIKitConfig(): UIKitConfig {
76
+ return currentConfig;
77
+ }
78
+
79
+ // 获取表单默认配置
80
+ export function getFormDefaults(): Partial<PFormProps> {
81
+ return {
82
+ labelCol: currentConfig.form?.labelCol,
83
+ wrapperCol: currentConfig.form?.wrapperCol,
84
+ };
85
+ }
86
+
87
+ // 获取Grid默认配置
88
+ export function getGridDefaults(): Partial<PGridProps> {
89
+ return {
90
+ align: currentConfig.grid?.align,
91
+ lazyReset: currentConfig.grid?.lazyReset,
92
+ fitHeight: currentConfig.grid?.fitHeight,
93
+ striped: currentConfig.grid?.striped,
94
+ };
95
+ }
96
+
97
+ // 获取CanvasTable默认配置
98
+ export function getCanvasTableDefaults(): ConfigType {
99
+ return currentConfig.canvasTable || ({} as ConfigType);
100
+ }
101
+
102
+ // 重置为默认配置
103
+ export function resetUIKitConfig(): void {
104
+ currentConfig = JSON.parse(JSON.stringify(defaultConfig));
105
+ }