@vue-ui-kit/ant 1.6.0 → 1.7.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.
@@ -5,11 +5,13 @@ declare const _default: <D = Recordable<any>, F = Recordable<any>>(__VLS_props:
5
5
  title: string;
6
6
  width: string | number;
7
7
  bodyStyle: Record<string, any>;
8
+ multipleAllowEmpty: boolean;
8
9
  }> & Omit<{
9
10
  readonly title: string;
10
11
  readonly width: string | number;
11
12
  readonly bodyStyle: Record<string, any>;
12
13
  readonly gridSetting: PGridProps<D, F>;
14
+ readonly multipleAllowEmpty: boolean;
13
15
  } & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps & Readonly<import('vue').ExtractPropTypes<{
14
16
  gridSetting: {
15
17
  type: PropType<PGridProps<D, F>>;
@@ -23,15 +25,23 @@ declare const _default: <D = Recordable<any>, F = Recordable<any>>(__VLS_props:
23
25
  type: PropType<string | number>;
24
26
  default: string;
25
27
  };
28
+ multipleAllowEmpty: {
29
+ type: BooleanConstructor;
30
+ default: boolean;
31
+ };
26
32
  bodyStyle: {
27
33
  type: ObjectConstructor;
28
34
  default: () => {};
29
35
  };
30
- }>> & Readonly<{}>, "title" | "width" | "bodyStyle">, "gridSetting" | ("title" | "width" | "bodyStyle")> & {}> & import('vue').PublicProps;
36
+ }>> & Readonly<{}>, "title" | "width" | "bodyStyle" | "multipleAllowEmpty">, "gridSetting" | ("title" | "width" | "bodyStyle" | "multipleAllowEmpty")> & {}> & import('vue').PublicProps;
31
37
  expose(exposed: import('vue').ShallowUnwrapRef<{
32
38
  pick: () => Promise<{
33
39
  row: D;
34
40
  field?: string;
41
+ } | D[]>;
42
+ pickMultiple: () => Promise<D[] | {
43
+ row: D;
44
+ field?: string;
35
45
  }>;
36
46
  }>): void;
37
47
  attrs: any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vue-ui-kit/ant",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Vue3 UI Kit based on Ant Design",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -4,6 +4,7 @@ import { ColProps } from 'ant-design-vue/lib/grid/Col';
4
4
  import { FormProps } from 'ant-design-vue/lib/form/Form';
5
5
  import { TableColumnType, TableProps, TooltipProps } from 'ant-design-vue';
6
6
  import { ButtonType } from 'ant-design-vue/lib/button/buttonTypes';
7
+ import { ComputedRef } from 'vue';
7
8
 
8
9
  export interface CellFuncArg<D = Recordable> {
9
10
  row: D;
@@ -89,7 +90,7 @@ export type GroupMenuItemHandler<F = Recordable> = ({
89
90
  }: {
90
91
  index: number;
91
92
  code: string;
92
- data: Partial<F>;
93
+ data: Partial<F & { __index: number }>;
93
94
  }) => void;
94
95
 
95
96
  export interface GroupMenuItem<F = Recordable> {
@@ -105,6 +106,8 @@ export interface PFormGroupProps<F = Recordable> {
105
106
  tabLabel?: string;
106
107
  editAble?: boolean;
107
108
  showAdd?: boolean;
109
+ /* 是否保持连续编号 */
110
+ keepSerial?: boolean;
108
111
  itemMenus?: Array<GroupMenuItem<F>>;
109
112
  creatItem?: ({ list }: { list?: Partial<F>[] }) => Promise<Partial<F>>;
110
113
  max?: number;
@@ -256,6 +259,7 @@ export interface PGridInstance<D = Recordable, F = Recordable> {
256
259
  passQuery: (query: Partial<F>, lazy?: boolean) => Promise<void | D[]>;
257
260
  };
258
261
  selectedRowKeys: string[] | number[];
262
+ selectedRecords: D[];
259
263
  $table: Recordable;
260
264
  $form: Recordable;
261
265
  setLoadings: (value: boolean) => void;
@@ -270,6 +274,7 @@ export interface PFormInstance {
270
274
 
271
275
  export interface PromisePickerInstance<D = Recordable> {
272
276
  pick: () => Promise<{ row: D; field?: string }>;
277
+ pickMultiple: () => Promise<D[]>;
273
278
  }
274
279
 
275
280
  export interface PFormBlockInstance {
@@ -1,8 +1,8 @@
1
1
  <script lang="ts" generic="F = Recordable" setup name="PFormGroup">
2
- import { computed, nextTick, PropType, ref } from 'vue';
2
+ import { computed, nextTick, PropType, ref, watchEffect } from 'vue';
3
3
  import { PFormGroupProps, PFormBlockInstance } from '#/antProxy';
4
4
  import { MoreOutlined } from '@ant-design/icons-vue';
5
- import { cloneDeep, toString, isFunction, omit } from 'lodash-es';
5
+ import { cloneDeep, toString, isFunction, omit, maxBy, debounce } from 'lodash-es';
6
6
  import PGroupBlock from '@/components/PGroupBlock.vue';
7
7
  import {
8
8
  Card as ACard,
@@ -13,10 +13,12 @@
13
13
  Menu as AMenu,
14
14
  MenuItem as AMenuItem,
15
15
  } from 'ant-design-vue';
16
+ import { valued } from '@/utils/is';
17
+ import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
16
18
 
17
19
  const props = defineProps<PFormGroupProps<F>>();
18
20
  const model = defineModel({
19
- type: Array as PropType<Partial<F>[]>,
21
+ type: Array as PropType<Partial<F & { __index: number }>[]>,
20
22
  default: () => [],
21
23
  });
22
24
  const activeKey = ref(0);
@@ -24,18 +26,38 @@
24
26
  const setActiveKey = (key: number) => {
25
27
  activeKey.value = key;
26
28
  };
27
- const maxLen = props.max ?? Infinity;
29
+ const maxLen = computed(() => props.max ?? Infinity);
28
30
  const handleAddItem = (idx: number) => {
29
31
  const creator = props.creatItem ?? (() => Promise.resolve({} as Partial<F>));
30
32
  creator({ list: model.value }).then((item) => {
31
- model.value = [...model.value, item];
33
+ model.value = [
34
+ ...model.value,
35
+ {
36
+ ...item,
37
+ __index: (maxBy(model.value, (m) => m.__index ?? -1)?.__index ?? -1) + 1,
38
+ } as Partial<F & { __index: number }>,
39
+ ];
32
40
  activeKey.value = idx;
33
41
  });
34
42
  };
35
- const itemMenus = props.itemMenus ?? [{ content: '删除', code: 'delete' }];
36
- const handleMenu = (key: string, item: Partial<F>, idx: number) => {
43
+ const itemMenus = props.itemMenus ?? [
44
+ { content: '复制', code: 'copy' },
45
+ { content: '删除', code: 'delete' },
46
+ ];
47
+ watchEffect(() => {
48
+ if (!props.keepSerial) {
49
+ const unSortItems = model.value.filter((f) => !valued(f.__index));
50
+ if (unSortItems.length > 0) {
51
+ unSortItems.forEach((item) => {
52
+ // @ts-ignore
53
+ item.__index = (maxBy(model.value, (m) => m.__index ?? -1)?.__index ?? -1) + 1;
54
+ });
55
+ }
56
+ }
57
+ });
58
+ const handleMenu = ({ key }: MenuInfo, item: Partial<F & { __index: number }>, idx: number) => {
37
59
  if (props.menuHandler && isFunction(props.menuHandler)) {
38
- props.menuHandler({ code: key, data: item, index: idx });
60
+ props.menuHandler({ code: toString(key), data: item, index: idx });
39
61
  } else {
40
62
  switch (key) {
41
63
  case 'delete':
@@ -48,11 +70,25 @@
48
70
  });
49
71
  break;
50
72
  case 'copy':
51
- model.value = [...model.value, cloneDeep(omit(item, ['id']))];
73
+ model.value = [
74
+ ...model.value,
75
+ cloneDeep({
76
+ ...omit(item, ['id', '__index']),
77
+ __index: (maxBy(model.value, (m) => m.__index ?? -1)?.__index ?? -1) + 1,
78
+ }),
79
+ ] as Partial<F & { __index: number }>[];
52
80
  break;
53
81
  }
54
82
  }
55
83
  };
84
+ const debounceHandleMenu = debounce(handleMenu, 100);
85
+ watchEffect(() => {
86
+ if (model.value.length === 0) {
87
+ activeKey.value = 0;
88
+ } else if (activeKey.value >= model.value.length) {
89
+ activeKey.value = model.value.length - 1;
90
+ }
91
+ });
56
92
  defineExpose({
57
93
  activeKey: computed(() => activeKey.value),
58
94
  setActiveKey,
@@ -69,9 +105,9 @@
69
105
  <template #rightExtra>
70
106
  <slot name="rightExtra">
71
107
  <a-button
108
+ v-if="showAdd && model.length < maxLen"
72
109
  type="link"
73
110
  @click="handleAddItem(model.length)"
74
- v-if="showAdd && model.length < maxLen"
75
111
  >
76
112
  + 新增
77
113
  </a-button>
@@ -80,14 +116,14 @@
80
116
  <a-tab-pane
81
117
  v-for="(item, idx) in model"
82
118
  :key="idx"
83
- :tab="`${tabLabel} ${idx + 1}`"
119
+ :tab="`${tabLabel} ${keepSerial ? idx + 1 : (item.__index ?? 0) + 1}`"
84
120
  force-render
85
121
  >
86
122
  <template #closeIcon>
87
123
  <a-dropdown v-if="editAble && itemMenus?.length">
88
124
  <MoreOutlined />
89
125
  <template #overlay>
90
- <a-menu @click="(e) => handleMenu(toString(e.key), item, idx)">
126
+ <a-menu @click="(e) => debounceHandleMenu(e, item, idx)">
91
127
  <a-menu-item
92
128
  v-for="menu in itemMenus.filter(
93
129
  (f) => f.visibleMethod?.({ data: item, index: idx }) ?? true,
@@ -529,7 +529,7 @@
529
529
  </template>
530
530
  <a-button
531
531
  :type="btn.type"
532
- :size="btn.size ?? 'small'"
532
+ :size="btn.size ?? 'middle'"
533
533
  :disabled="toolbarConfig.disabled || btn.disabled"
534
534
  :loading="loading.toolbar || (!!btn.code && codeLoadings[btn.code])"
535
535
  >
@@ -541,7 +541,7 @@
541
541
  <a-button
542
542
  v-else-if="btn.code"
543
543
  :type="btn.type"
544
- :size="btn.size ?? 'small'"
544
+ :size="btn.size ?? 'middle'"
545
545
  :disabled="toolbarConfig.disabled || btn.disabled"
546
546
  :loading="loading.toolbar || (!!btn.code && codeLoadings[btn.code])"
547
547
  @click="debounceToolBtnClick(btn.code)"
@@ -559,7 +559,7 @@
559
559
  v-for="(tool, idx) in toolbarConfig.tools"
560
560
  :key="idx"
561
561
  :type="tool.type"
562
- size="small"
562
+ :size="btn.size ?? 'middle'"
563
563
  :disabled="toolbarConfig.disabled || tool.disabled"
564
564
  @click="debounceToolToolClick(tool.code)"
565
565
  :loading="loading.toolbar || (!!tool.code && codeLoadings[tool.code])"
@@ -1,10 +1,12 @@
1
1
  <script lang="ts" setup name="PromisePicker" generic="D = Recordable, F = Recordable">
2
- import { PropType, reactive, ref } from 'vue';
2
+ import { PropType, reactive, ref, computed } from 'vue';
3
3
  import { PGridInstance, PGridProps } from '#/antProxy';
4
4
  import PGrid from '@/components/PGrid.vue';
5
+ import { $warning } from '@/hooks/useMessage';
6
+ import { Button as AButton, Modal as AModal } from 'ant-design-vue';
5
7
 
6
8
  const gridEl = ref<PGridInstance<D>>();
7
- defineProps({
9
+ const props = defineProps({
8
10
  gridSetting: {
9
11
  type: Object as PropType<PGridProps<D, F>>,
10
12
  required: true,
@@ -17,14 +19,20 @@
17
19
  type: [String, Number] as PropType<string | number>,
18
20
  default: '70%',
19
21
  },
22
+ multipleAllowEmpty: {
23
+ type: Boolean,
24
+ default: false,
25
+ },
20
26
  bodyStyle: {
21
27
  type: Object,
22
28
  default: () => ({}),
23
29
  },
24
30
  });
31
+ const isMultiple = computed(() => props.gridSetting.selectConfig?.multiple);
25
32
  let resolvePromise: (
26
33
  value: { row: D; field?: string } | PromiseLike<{ row: D; field?: string }>,
27
34
  ) => void;
35
+ let multipleResolver: (rows: D[]) => void;
28
36
  let rejectPromise: (reason?: any) => void;
29
37
  const visible = reactive({
30
38
  modal: false,
@@ -34,19 +42,42 @@
34
42
  rejectPromise();
35
43
  }
36
44
  };
45
+
46
+ const manualCancel = () => {
47
+ visible.modal = false;
48
+ if (rejectPromise) {
49
+ rejectPromise();
50
+ }
51
+ };
37
52
  const selectRow = ({ row, field }: { row: D; field?: string }) => {
38
- if (resolvePromise) {
53
+ // 单选才关闭结束选择
54
+ if (resolvePromise && !isMultiple.value) {
39
55
  visible.modal = false;
40
56
  resolvePromise({ row, field });
41
57
  }
42
58
  };
59
+ const endMultiplePicker = () => {
60
+ const records = gridEl.value?.selectedRecords ?? [];
61
+ if (records.length === 0 && !props.multipleAllowEmpty) {
62
+ $warning('请选择数据');
63
+ } else {
64
+ visible.modal = false;
65
+ multipleResolver(records);
66
+ }
67
+ };
43
68
  defineExpose({
44
69
  pick: () =>
45
- new Promise<{ row: D; field?: string }>((resolve, reject) => {
70
+ new Promise<{ row: D; field?: string } | D[]>((resolve, reject) => {
46
71
  resolvePromise = resolve;
47
72
  rejectPromise = reject;
48
73
  visible.modal = true;
49
74
  }),
75
+ pickMultiple: () =>
76
+ new Promise<{ row: D; field?: string } | D[]>((resolve, reject) => {
77
+ multipleResolver = resolve;
78
+ rejectPromise = reject;
79
+ visible.modal = true;
80
+ }),
50
81
  });
51
82
  </script>
52
83
  <template>
@@ -55,10 +86,18 @@
55
86
  destroy-on-close
56
87
  :title="title"
57
88
  :width="width"
58
- :footer="null"
89
+ :footer="isMultiple ? undefined : null"
59
90
  @cancel="handleCancel"
60
91
  :body-style="bodyStyle"
61
92
  >
62
93
  <p-grid v-bind="gridSetting" ref="gridEl" @pick="selectRow" />
94
+ <template v-if="isMultiple" #footer>
95
+ <div class="w-full text-right p-2">
96
+ <span>
97
+ <a-button class="mr-4" @click="manualCancel"> 取消 </a-button>
98
+ <a-button type="primary" @click="endMultiplePicker"> 确定 </a-button>
99
+ </span>
100
+ </div>
101
+ </template>
63
102
  </a-modal>
64
103
  </template>