@vue-ui-kit/ant 1.3.0 → 1.5.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.
@@ -1,34 +1,74 @@
1
1
  import { PGridProps } from '../../declarations/antProxy';
2
2
  declare const _default: <D = Recordable<any>, F = Recordable<any>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_Prettify<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
3
3
  props: __VLS_Prettify<Pick<Partial<{}> & Omit<{
4
- readonly onReset?: ((...args: any[]) => any) | undefined;
5
- readonly onPick?: ((...args: any[]) => any) | undefined;
6
- readonly onQuery?: ((...args: any[]) => any) | undefined;
7
- readonly "onUpdate:tableData"?: ((...args: any[]) => any) | undefined;
8
- readonly onToolbarButtonClick?: ((...args: any[]) => any) | undefined;
9
- readonly onToolbarToolClick?: ((...args: any[]) => any) | undefined;
4
+ readonly onPick?: ((data: {
5
+ row: D;
6
+ field: string;
7
+ }) => any) | undefined;
8
+ readonly onToolbarButtonClick?: ((data: {
9
+ data: D[];
10
+ code: string;
11
+ selectedKeys: Array<string | number>;
12
+ records: D[];
13
+ }) => any) | undefined;
14
+ readonly onToolbarToolClick?: ((data: {
15
+ data: D[];
16
+ code: string;
17
+ selectedKeys: Array<string | number>;
18
+ records: D[];
19
+ }) => any) | undefined;
10
20
  } & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps & Readonly<{}> & Readonly<{
11
- onReset?: ((...args: any[]) => any) | undefined;
12
- onPick?: ((...args: any[]) => any) | undefined;
13
- onQuery?: ((...args: any[]) => any) | undefined;
14
- "onUpdate:tableData"?: ((...args: any[]) => any) | undefined;
15
- onToolbarButtonClick?: ((...args: any[]) => any) | undefined;
16
- onToolbarToolClick?: ((...args: any[]) => any) | undefined;
17
- }>, never>, "onReset" | "onPick" | "onQuery" | "onUpdate:tableData" | "onToolbarButtonClick" | "onToolbarToolClick"> & PGridProps<D, F>> & import('vue').PublicProps;
21
+ onPick?: ((data: {
22
+ row: D;
23
+ field: string;
24
+ }) => any) | undefined;
25
+ onToolbarButtonClick?: ((data: {
26
+ data: D[];
27
+ code: string;
28
+ selectedKeys: Array<string | number>;
29
+ records: D[];
30
+ }) => any) | undefined;
31
+ onToolbarToolClick?: ((data: {
32
+ data: D[];
33
+ code: string;
34
+ selectedKeys: Array<string | number>;
35
+ records: D[];
36
+ }) => any) | undefined;
37
+ }>, never>, "onPick" | "onToolbarButtonClick" | "onToolbarToolClick"> & PGridProps<D, F>> & import('vue').PublicProps;
18
38
  expose(exposed: import('vue').ShallowUnwrapRef<{
19
39
  commitProxy: {
20
40
  query: import('lodash').DebouncedFunc<() => Promise<D[]>>;
21
41
  reload: () => void;
22
42
  reloadPage: () => Promise<D[]>;
23
- passQuery: (params: Partial<F>) => Promise<D[]> | undefined;
43
+ passQuery: (params: Partial<F>, lazy?: boolean) => Promise<void> | Promise<D[]> | undefined;
24
44
  };
25
45
  $table: import('vue').ComputedRef<any>;
26
- selectedRowKeys: import('vue').ComputedRef<string[] | number[]>;
46
+ selectedRowKeys: import('vue').ComputedRef<(string | number)[]>;
47
+ setBtnLoading: (code: string, value: boolean) => void;
48
+ selectedRecords: import('vue').ComputedRef<D[]>;
27
49
  $form: import('vue').ComputedRef<any>;
50
+ setLoadings: (value: boolean) => void;
28
51
  }>): void;
29
52
  attrs: any;
30
53
  slots: ReturnType<() => {}>;
31
- emit: ((event: "reset" | "pick" | "query" | "update:tableData" | "toolbarButtonClick" | "toolbarToolClick", ...args: any[]) => void) & {};
54
+ emit: {
55
+ (event: "toolbarButtonClick", data: {
56
+ data: D[];
57
+ code: string;
58
+ selectedKeys: Array<string | number>;
59
+ records: D[];
60
+ }): void;
61
+ (event: "toolbarToolClick", data: {
62
+ data: D[];
63
+ code: string;
64
+ selectedKeys: Array<string | number>;
65
+ records: D[];
66
+ }): void;
67
+ (event: "pick", data: {
68
+ row: D;
69
+ field: string;
70
+ }): void;
71
+ } & {};
32
72
  }>) => import('vue').VNode<import('vue').RendererNode, import('vue').RendererElement, {
33
73
  [key: string]: any;
34
74
  }> & {
@@ -1,2 +1,4 @@
1
1
  export declare const noValue: (val: any) => boolean;
2
2
  export declare const valued: (val: any) => boolean;
3
+ export declare const isBadValue: (val: any) => boolean;
4
+ export declare const isGoodValue: (val: any) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vue-ui-kit/ant",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Vue3 UI Kit based on Ant Design",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -58,6 +58,7 @@ export interface PFormItemProps<F = Recordable> {
58
58
  itemRender?: ItemRender;
59
59
  tooltipConfig?: TooltipConfig;
60
60
  slots?: {
61
+ title?: () => any;
61
62
  default?: (
62
63
  { data, field }: ItemFuncArg<F>,
63
64
  passTrigger?: (cusFields?: string | string[]) => void,
@@ -132,6 +133,8 @@ export interface PButtonProps extends ButtonProps {
132
133
 
133
134
  export interface ToolbarButtonProps extends PButtonProps {
134
135
  code: string;
136
+ dropdowns?: ToolbarButtonProps[];
137
+ size?: 'small' | 'large' | 'middle';
135
138
  }
136
139
 
137
140
  export interface ToolbarConfig {
@@ -151,7 +154,7 @@ export interface ResponsePathConfig<D = Recordable> {
151
154
  | ((res: Recordable) => string | { status: string; content: string; icon?: string });
152
155
  }
153
156
 
154
- declare type HandlerMulti = (ids: string[] | number[]) => any;
157
+ declare type HandlerMulti = (ids: Array<string | number>) => any;
155
158
 
156
159
  export interface AjaxConfig<F = Recordable> {
157
160
  query: (Q: { page?: IPage; form: Partial<F> }) => Promise<Recordable>;
@@ -169,16 +172,17 @@ export interface PageConfig {
169
172
  pageSize?: number;
170
173
  }
171
174
 
172
- export interface SelectConfig {
175
+ export interface SelectConfig<D = Recordable> {
173
176
  multiple?: boolean;
177
+ getCheckboxProps?: (record: D) => { disabled?: boolean };
174
178
  }
175
179
 
176
180
  export type PGridProps<D = Recordable, F = Recordable> = {
177
- selectConfig?: SelectConfig;
181
+ selectConfig?: SelectConfig<D>;
178
182
  rowKey?: string;
179
183
  manualFetch?: boolean;
180
184
  formConfig?: PFormProps<F>;
181
- columns: ColumnProps<D>[];
185
+ columns?: ColumnProps<D>[];
182
186
  toolbarConfig?: ToolbarConfig;
183
187
  pageConfig?: PageConfig;
184
188
  proxyConfig?: ProxyConfig<D, F>;
@@ -248,11 +252,13 @@ export interface PGridInstance<D = Recordable, F = Recordable> {
248
252
  query: () => Promise<D[]>;
249
253
  reload: () => Promise<D[]>;
250
254
  reloadPage: () => Promise<D[]>;
251
- passQuery: (query: Partial<F>) => Promise<D[]>;
255
+ passQuery: (query: Partial<F>, lazy?: boolean) => Promise<void | D[]>;
252
256
  };
253
257
  selectedRowKeys: string[] | number[];
254
258
  $table: Recordable;
255
259
  $form: Recordable;
260
+ setLoadings: (value: boolean) => void;
261
+ setBtnLoading: (code: string, status: boolean) => void;
256
262
  }
257
263
 
258
264
  export interface PFormInstance {
@@ -5,7 +5,7 @@
5
5
  import { v4 as uuid_v4 } from 'uuid';
6
6
  import RenderAntItem from '@/components/RenderAntItem';
7
7
  import RenderItemSlots from '@/components/RenderItemSlots';
8
- import { valued } from '@/utils/is';
8
+ import { isGoodValue, valued } from '@/utils/is';
9
9
  import { eachTree } from '@/utils/treeHelper';
10
10
  import { defaultItemResponsive } from '@/utils/core';
11
11
  import {
@@ -39,7 +39,7 @@
39
39
  const obj: Partial<F> = {};
40
40
  eachTree(items.value, (item) => {
41
41
  if (item.field && item.itemRender) {
42
- if (valued(item.itemRender.defaultValue)) {
42
+ if (isGoodValue(item.itemRender.defaultValue)) {
43
43
  obj[item.field] = item.itemRender.defaultValue;
44
44
  } else {
45
45
  obj[item.field] = undefined;
@@ -103,6 +103,9 @@
103
103
  ])
104
104
  "
105
105
  >
106
+ <template v-if="item.slots?.title" #label>
107
+ <component :is="item.slots.title" />
108
+ </template>
106
109
  <render-item-slots
107
110
  v-if="item.slots?.default"
108
111
  :form-data="formData"
@@ -8,7 +8,7 @@
8
8
  import RenderTitleSlots from '@/components/RenderTitleSlots';
9
9
  import RenderDefaultSlots from '@/components/RenderDefaultSlots';
10
10
  import { v4 as uuid_v4 } from 'uuid';
11
- import { valued } from '@/utils/is';
11
+ import { isGoodValue, valued } from '@/utils/is';
12
12
  import RenderItemSlots from '@/components/RenderItemSlots';
13
13
  import { cleanCol, defaultItemResponsive, defaultLabelCol } from '@/utils/core';
14
14
  import Icon from '@/renders/Icon';
@@ -24,7 +24,7 @@
24
24
  Tooltip as ATooltip,
25
25
  } from 'ant-design-vue';
26
26
  import { TablePaginationConfig } from 'ant-design-vue/es/table/interface';
27
- import { InfoCircleOutlined } from '@ant-design/icons-vue';
27
+ import { InfoCircleOutlined, DownOutlined } from '@ant-design/icons-vue';
28
28
 
29
29
  const props = defineProps<PGridProps<D, F>>();
30
30
  const {
@@ -39,12 +39,14 @@
39
39
  } = toRefs(props);
40
40
  const loading = reactive({
41
41
  table: false,
42
+ toolbar: false,
42
43
  form: false,
43
44
  });
44
45
  const submitOnReset = true;
45
46
  const boxEl = ref<HTMLDivElement>();
46
47
  const renderHeight = ref(500);
47
- const selectedRowKeys = ref<string[] | number[]>([]);
48
+ const selectedRowKeys = ref<Array<string | number>>([]);
49
+ const selectedCaches = ref<D[]>([]);
48
50
  const innerToolbarHandler = (code: string) => {
49
51
  const { ajax } = proxyConfig.value!;
50
52
  switch (code) {
@@ -56,6 +58,7 @@
56
58
  content: '确认删除选中的数据吗?',
57
59
  }).then(() => {
58
60
  loading.table = true;
61
+ loading.toolbar = true;
59
62
  ajax.multiDelete!(selectedRowKeys.value)
60
63
  .then(() => {
61
64
  $message.success('删除成功');
@@ -66,6 +69,8 @@
66
69
  })
67
70
  .finally(() => {
68
71
  loading.table = false;
72
+ loading.toolbar = false;
73
+ codeLoadings.multiDelete = false;
69
74
  });
70
75
  });
71
76
  } else {
@@ -77,7 +82,7 @@
77
82
  };
78
83
  const slotTitleColumns = computed(() => {
79
84
  const cols: ColumnProps<D>[] = [];
80
- eachTree(columns.value, (col) => {
85
+ eachTree(columns.value ?? [], (col) => {
81
86
  if (col.slots && col.slots.title) {
82
87
  if (!col.field) {
83
88
  col.field = '__holder__' + cols.length;
@@ -94,6 +99,21 @@
94
99
  const refreshForm = () => {
95
100
  renderFormKey.value = uuid_v4();
96
101
  };
102
+ const codeLoadings = reactive<Record<string, boolean>>(
103
+ [
104
+ ...(props.toolbarConfig?.buttons?.map((m) => m.code) ?? []).filter((f) => f),
105
+ ...(props.toolbarConfig?.tools?.map((m) => m.code) ?? []).filter((f) => f),
106
+ ].reduce(
107
+ (acc, cur) => {
108
+ acc[cur!] = false;
109
+ return acc;
110
+ },
111
+ {} as Record<string, boolean>,
112
+ ),
113
+ );
114
+ const setBtnLoading = (code: string, value: boolean) => {
115
+ codeLoadings[code] = value;
116
+ };
97
117
  const refreshTable = () => {
98
118
  renderTableKey.value = uuid_v4();
99
119
  };
@@ -101,7 +121,7 @@
101
121
  const debounceRefreshTable = debounce(refreshTable, 100);
102
122
  const slotDefaultColumns = computed(() => {
103
123
  const cols: ColumnProps<D>[] = [];
104
- eachTree(columns.value, (col) => {
124
+ eachTree(columns.value ?? [], (col) => {
105
125
  if ((col.slots && col.slots.default) || col.formatter || col.cellRender) {
106
126
  if (!col.field) {
107
127
  col.field = '__holder__' + cols.length;
@@ -126,26 +146,44 @@
126
146
  : 'bad',
127
147
  );
128
148
  const attrs = useAttrs();
129
- const emit = defineEmits([
130
- 'query',
131
- 'reset',
132
- 'update:tableData',
133
- 'toolbarButtonClick',
134
- 'toolbarToolClick',
135
- 'pick',
136
- ]);
149
+ const emit = defineEmits<{
150
+ (
151
+ event: 'toolbarButtonClick',
152
+ data: { data: D[]; code: string; selectedKeys: Array<string | number>; records: D[] },
153
+ ): void;
154
+ (
155
+ event: 'toolbarToolClick',
156
+ data: { data: D[]; code: string; selectedKeys: Array<string | number>; records: D[] },
157
+ ): void;
158
+ (event: 'pick', data: { row: D; field: string }): void;
159
+ }>();
160
+ // @ts-ignore
161
+ const selectedRecords = computed<D[]>(() =>
162
+ selectedCaches.value.filter((f) =>
163
+ selectedRowKeys.value.includes(f[props.rowKey ?? 'id'] as string | number),
164
+ ),
165
+ );
137
166
  const toolBtnClick = (code: string) => {
138
167
  emit('toolbarButtonClick', {
139
168
  data: tableData.value,
140
169
  code,
141
170
  selectedKeys: selectedRowKeys.value,
171
+ records: selectedRecords.value,
142
172
  });
143
173
  innerToolbarHandler(code);
144
174
  };
175
+ const debounceToolBtnClick = debounce(toolBtnClick, 100);
176
+ const toolBtnMenuClick = ({ key }) => debounceToolBtnClick(key);
145
177
  const toolToolClick = (code: string) => {
146
- emit('toolbarToolClick', { data: tableData.value, code, selectedKeys: selectedRowKeys.value });
178
+ emit('toolbarToolClick', {
179
+ data: tableData.value,
180
+ code,
181
+ selectedKeys: selectedRowKeys.value,
182
+ records: selectedRecords.value,
183
+ });
147
184
  innerToolbarHandler(code);
148
185
  };
186
+ const debounceToolToolClick = debounce(toolToolClick, 100);
149
187
  const queryFormData = ref<Partial<F>>({}) as Ref<Partial<F>>;
150
188
  const pickRow = ({ row, field }: { row: D; field: string }) => {
151
189
  emit('pick', { row, field });
@@ -158,7 +196,7 @@
158
196
  const obj: Partial<F> = {};
159
197
  eachTree(formConfig.value.items, (item) => {
160
198
  if (item.field && item.itemRender) {
161
- if (valued(item.itemRender.defaultValue)) {
199
+ if (isGoodValue(item.itemRender.defaultValue)) {
162
200
  obj[item.field] = item.itemRender.defaultValue;
163
201
  } else {
164
202
  obj[item.field] = undefined;
@@ -208,6 +246,7 @@
208
246
  };
209
247
  const resetPage = () => {
210
248
  pagination.page = 1;
249
+ selectedCaches.value = [];
211
250
  selectedRowKeys.value = [];
212
251
  return fetchData();
213
252
  };
@@ -266,10 +305,10 @@
266
305
  }
267
306
  });
268
307
  const debounceFetchData = debounce(fetchData, 160);
269
- const passQuery = (params: Partial<F>) => {
308
+ const passQuery = (params: Partial<F>, lazy?: boolean) => {
270
309
  Object.assign(queryFormData.value, params);
271
310
  pagination.page = 1;
272
- return debounceFetchData();
311
+ return lazy ? Promise.resolve() : debounceFetchData();
273
312
  };
274
313
  const pg = computed(() =>
275
314
  mode.value === 'pagination'
@@ -303,7 +342,16 @@
303
342
  selectedRowKeys: selectedRowKeys.value,
304
343
  onChange: (selectedKeys: string[] | number[]) => {
305
344
  selectedRowKeys.value = selectedKeys;
345
+ const primaryKey = props.rowKey ?? 'id';
346
+ const cachedKeys = selectedCaches.value.map((m) => m[primaryKey]);
347
+ const newKeys = selectedKeys.filter((f) => !cachedKeys.includes(f));
348
+ const newRecords: D[] = newKeys
349
+ .map((m) => tableData.value.find((f) => f[primaryKey] === m))
350
+ .filter((f) => !!f) as D[];
351
+ // @ts-ignore
352
+ selectedCaches.value = [...selectedCaches.value, ...newRecords];
306
353
  },
354
+ getCheckboxProps: selectConfig.value.getCheckboxProps,
307
355
  },
308
356
  }
309
357
  : {},
@@ -338,7 +386,14 @@
338
386
  },
339
387
  $table: computed(() => tableEl.value),
340
388
  selectedRowKeys: computed(() => selectedRowKeys.value),
389
+ setBtnLoading,
390
+ selectedRecords,
341
391
  $form: computed(() => formEl.value),
392
+ setLoadings: (value: boolean) => {
393
+ loading.form = value;
394
+ loading.table = value;
395
+ loading.toolbar = value;
396
+ },
342
397
  });
343
398
  onMounted(() => {
344
399
  /*判断本组件所在容器DOM*/
@@ -438,16 +493,37 @@
438
493
  >
439
494
  <div class="flex items-center flex-1 gap-4px">
440
495
  <template v-if="toolbarConfig.buttons && toolbarConfig.buttons.length > 0">
441
- <a-button
442
- v-for="(btn, idx) in toolbarConfig.buttons"
443
- :key="idx"
444
- :type="btn.type"
445
- size="small"
446
- @click="toolBtnClick(btn.code)"
447
- >
448
- <Icon v-if="btn.icon" :icon="btn.icon" />
449
- {{ btn.content }}
450
- </a-button>
496
+ <template v-for="(btn, idx) in toolbarConfig.buttons" :key="idx">
497
+ <a-dropdown v-if="btn.dropdowns && btn.dropdowns.length">
498
+ <template #overlay>
499
+ <a-menu @click="toolBtnMenuClick">
500
+ <a-menu-item v-for="sub in btn.dropdowns" :key="sub.code"
501
+ >{{ sub.content }}
502
+ </a-menu-item>
503
+ </a-menu>
504
+ </template>
505
+ <a-button
506
+ :type="btn.type"
507
+ :size="btn.size ?? 'small'"
508
+ :loading="loading.toolbar || (!!btn.code && codeLoadings[btn.code])"
509
+ >
510
+ <Icon v-if="btn.icon" :icon="btn.icon" />
511
+ {{ btn.content }}
512
+ <DownOutlined />
513
+ </a-button>
514
+ </a-dropdown>
515
+ <a-button
516
+ v-else-if="btn.code"
517
+ :type="btn.type"
518
+ :size="btn.size ?? 'small'"
519
+ :loading="loading.toolbar || (!!btn.code && codeLoadings[btn.code])"
520
+ @click="debounceToolBtnClick(btn.code)"
521
+ >
522
+ <Icon v-if="btn.icon" :icon="btn.icon" />
523
+ {{ btn.content }}
524
+ </a-button>
525
+ <div v-else></div>
526
+ </template>
451
527
  </template>
452
528
  </div>
453
529
  <span class="flex items-center gap-4px">
@@ -457,7 +533,8 @@
457
533
  :key="idx"
458
534
  :type="tool.type"
459
535
  size="small"
460
- @click="toolToolClick(tool.code)"
536
+ @click="debounceToolToolClick(tool.code)"
537
+ :loading="loading.toolbar || (!!tool.code && codeLoadings[tool.code])"
461
538
  >
462
539
  <Icon :icon="tool.icon" />
463
540
  </a-button>
@@ -469,7 +546,7 @@
469
546
  :key="renderTableKey + '_table'"
470
547
  :row-key="rowKey ?? 'id'"
471
548
  ref="tableEl"
472
- :columns="columns.map((c) => cleanCol(c as Recordable))"
549
+ :columns="(columns ?? []).map((c) => cleanCol(c as Recordable))"
473
550
  :data-source="tableData"
474
551
  :loading="loading.table"
475
552
  :pagination="pg"
@@ -23,7 +23,7 @@ import {
23
23
  } from 'ant-design-vue';
24
24
  import { set, isFunction, merge, omit } from 'lodash-es';
25
25
  import { ButtonProps } from 'ant-design-vue/lib/button';
26
- import { noValue, valued } from '@/utils/is';
26
+ import { isBadValue, isGoodValue, noValue, valued } from '@/utils/is';
27
27
  import TableInput from '@/renders/TableInput.vue';
28
28
  import Icon from '@/renders/Icon';
29
29
  import { computed } from 'vue';
@@ -130,7 +130,7 @@ const renderBasic = (name: string) => {
130
130
  { props = {}, attrs = {}, events = {}, defaultValue }: RenderOptions,
131
131
  { data, field }: RenderFormParams,
132
132
  ) {
133
- if (valued(defaultValue) && valued(field) && noValue(data[field!])) {
133
+ if (isGoodValue(defaultValue) && valued(field) && isBadValue(data[field!])) {
134
134
  data[field!] = defaultValue;
135
135
  }
136
136
  return field ? (
@@ -151,7 +151,7 @@ const renderBasic = (name: string) => {
151
151
  { props = antDefaultProps[name] ?? {}, attrs = {}, events = {}, defaultValue }: RenderOptions,
152
152
  { data, row, field }: RenderTableParams,
153
153
  ) {
154
- if (valued(defaultValue) && valued(field) && noValue(row[field!])) {
154
+ if (isGoodValue(defaultValue) && valued(field) && isBadValue(row[field!])) {
155
155
  row[field!] = defaultValue;
156
156
  }
157
157
  return field ? (
@@ -1,2 +1,4 @@
1
1
  export const noValue = (val: any) => val === null || val === undefined || val === '';
2
2
  export const valued = (val: any) => !noValue(val);
3
+ export const isBadValue = (val: any) => val === null || val === undefined;
4
+ export const isGoodValue = (val: any) => !isBadValue(val);