@vue-ui-kit/ant 1.2.0 → 1.4.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.
@@ -20,11 +20,12 @@ declare const _default: <D = Recordable<any>, F = Recordable<any>>(__VLS_props:
20
20
  query: import('lodash').DebouncedFunc<() => Promise<D[]>>;
21
21
  reload: () => void;
22
22
  reloadPage: () => Promise<D[]>;
23
- passQuery: (params: Partial<F>) => Promise<D[]> | undefined;
23
+ passQuery: (params: Partial<F>, lazy?: boolean) => Promise<void> | Promise<D[]> | undefined;
24
24
  };
25
25
  $table: import('vue').ComputedRef<any>;
26
26
  selectedRowKeys: import('vue').ComputedRef<string[] | number[]>;
27
27
  $form: import('vue').ComputedRef<any>;
28
+ setLoadings: (value: boolean) => void;
28
29
  }>): void;
29
30
  attrs: any;
30
31
  slots: ReturnType<() => {}>;
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- @charset "UTF-8";:root{--p-theme-bg: #fff}.p-pane{background-color:var(--p-theme-bg);padding:16px;border-radius:4px}.w-full{width:100%}.h-0{height:0}.h-full{height:100%}.flex{display:flex}.flex-col{flex-direction:column}.flex-1{flex:1}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-4px{gap:4px}.gap-8px{gap:8px}.pt-8px{padding-top:8px}.px-16px{padding-left:16px;padding-right:16px}.mt-4px{margin-top:4px}.text-right{text-align:right}.overflow-y-auto{overflow-y:auto}.p-theme-bg{background-color:var(--p-theme-bg)}.p-wrapper .p-form-wrapper{z-index:4}.p-wrapper .p-toolbar-wrapper{box-shadow:0 8px #fff;border-radius:.5em .5em 0 0;margin-bottom:-.5em}.p-wrapper .ant-form .ant-form-item{margin-bottom:0}.p-wrapper .ant-form .ant-form-item .ant-form-item-label{display:flex;align-items:start;justify-content:flex-end}.p-wrapper .ant-form .ant-form-item .ant-form-item-label>label{white-space:break-spaces;align-items:flex-start;height:fit-content;padding-top:.36em}.p-wrapper .ant-form .ant-form-item .ant-form-item-label>label:after{padding-left:4px;margin-inline-start:unset}.p-wrapper .ant-form.ant-form-vertical .ant-form-item .ant-form-item-label{justify-content:flex-start;font-weight:600}.p-wrapper .ant-table-sticky-scroll{visibility:hidden}.p-wrapper .ant-table-header.ant-table-sticky-holder:before{content:"";width:100%;position:absolute;height:22px;display:block;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);top:-16px}.p-wrapper .ant-table-wrapper .ant-table-pagination.ant-pagination{margin:unset;padding:16px 0}.p-wrapper .p-inner-scroll>.ant-table-wrapper{height:100%}.p-wrapper .p-inner-scroll>.ant-table-wrapper>.ant-spin-nested-loading{height:100%}.p-wrapper .p-inner-scroll>.ant-table-wrapper>.ant-spin-nested-loading>.ant-spin-container{height:100%;display:flex;flex-direction:column}.p-wrapper .p-inner-scroll>.ant-table-wrapper>.ant-spin-nested-loading>.ant-spin-container>.ant-table{border-radius:unset}.p-content-align-right.ant-form-item .ant-form-item-control-input-content{display:flex;justify-content:flex-end;align-items:center;text-align:right}.p-content-align-center.ant-form-item .ant-form-item-control-input-content{display:flex;justify-content:center;align-items:center;text-align:center}
1
+ @charset "UTF-8";:root{--p-theme-bg: #fff}.p-pane{background-color:var(--p-theme-bg);padding:16px;border-radius:4px}.w-full{width:100%}.cursor-pointer{cursor:pointer}.py-4x{padding-top:4px;padding-bottom:4px}.px-2x{padding-left:2px;padding-right:2px}.h-0{height:0}.h-full{height:100%}.flex{display:flex}.flex-col{flex-direction:column}.flex-1{flex:1}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-4px{gap:4px}.gap-8px{gap:8px}.pt-8px{padding-top:8px}.px-16px{padding-left:16px;padding-right:16px}.mt-4px{margin-top:4px}.text-right{text-align:right}.overflow-y-auto{overflow-y:auto}.p-theme-bg{background-color:var(--p-theme-bg)}.p-wrapper .p-form-wrapper{z-index:4}.p-wrapper .p-toolbar-wrapper{box-shadow:0 8px #fff;border-radius:.5em .5em 0 0;margin-bottom:-.5em}.p-wrapper .ant-form .ant-form-item{margin-bottom:0}.p-wrapper .ant-form .ant-form-item.p-required .ant-form-item-label>label:before{display:inline-block;margin-inline-end:4px;color:#ff4d4f;font-size:14px;font-family:SimSun,sans-serif;line-height:1;content:"*"}.p-wrapper .ant-form .ant-form-item .ant-form-item-label{display:flex;align-items:start;justify-content:flex-end}.p-wrapper .ant-form .ant-form-item .ant-form-item-label>label{white-space:break-spaces;align-items:flex-start;height:fit-content;padding-top:.36em}.p-wrapper .ant-form .ant-form-item .ant-form-item-label>label:after{padding-left:4px;margin-inline-start:unset}.p-wrapper .ant-form.ant-form-vertical .ant-form-item .ant-form-item-label{justify-content:flex-start;font-weight:600}.p-wrapper .ant-table-sticky-scroll{visibility:hidden}.p-wrapper .ant-table-header.ant-table-sticky-holder:before{content:"";width:100%;position:absolute;height:22px;display:block;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);top:-16px}.p-wrapper .ant-table-wrapper .ant-table-pagination.ant-pagination{margin:unset;padding:16px 0}.p-wrapper .p-inner-scroll>.ant-table-wrapper{height:100%}.p-wrapper .p-inner-scroll>.ant-table-wrapper>.ant-spin-nested-loading{height:100%}.p-wrapper .p-inner-scroll>.ant-table-wrapper>.ant-spin-nested-loading>.ant-spin-container{height:100%;display:flex;flex-direction:column}.p-wrapper .p-inner-scroll>.ant-table-wrapper>.ant-spin-nested-loading>.ant-spin-container>.ant-table{border-radius:unset}.p-wrapper .ant-pagination-options .ant-select{width:100px}.p-content-align-right.ant-form-item .ant-form-item-control-input-content{display:flex;justify-content:flex-end;align-items:center;text-align:right}.p-content-align-center.ant-form-item .ant-form-item-control-input-content{display:flex;justify-content:center;align-items:center;text-align:center}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vue-ui-kit/ant",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Vue3 UI Kit based on Ant Design",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -21,7 +21,6 @@
21
21
  "build": "ts-patch install && vite build",
22
22
  "prepack": "rimraf dist && yarn build",
23
23
  "prepare": "husky",
24
- "publish": "npm publish",
25
24
  "lint:lint-staged": "lint-staged",
26
25
  "lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\""
27
26
  },
@@ -1,9 +1,9 @@
1
- import type { Rule } from 'ant-design-vue/lib/form'
2
- import { ButtonProps } from 'ant-design-vue/lib/button'
3
- import { ColProps } from 'ant-design-vue/lib/grid/Col'
4
- import { FormProps } from 'ant-design-vue/lib/form/Form'
5
- import { TableColumnType, TableProps } from 'ant-design-vue'
6
- import { ButtonType } from 'ant-design-vue/lib/button/buttonTypes'
1
+ import type { Rule } from 'ant-design-vue/lib/form';
2
+ import { ButtonProps } from 'ant-design-vue/lib/button';
3
+ import { ColProps } from 'ant-design-vue/lib/grid/Col';
4
+ import { FormProps } from 'ant-design-vue/lib/form/Form';
5
+ import { TableColumnType, TableProps, TooltipProps } from 'ant-design-vue';
6
+ import { ButtonType } from 'ant-design-vue/lib/button/buttonTypes';
7
7
 
8
8
  export interface CellFuncArg<D = Recordable> {
9
9
  row: D;
@@ -42,15 +42,21 @@ export interface Responsive {
42
42
  xxl?: number;
43
43
  }
44
44
 
45
+ export interface TooltipConfig extends TooltipProps {
46
+ title: TooltipProps['title'] | (() => any);
47
+ }
48
+
45
49
  export interface PFormItemProps<F = Recordable> {
46
50
  field?: string;
47
51
  title?: string;
48
52
  span?: number;
49
- colon?: boolean
53
+ colon?: boolean;
54
+ forceRequired?: boolean; // 在页面需要必填标示,但field不需要必填校验时使用
50
55
  align?: 'left' | 'right' | 'center';
51
56
  col?: ColProps;
52
57
  rule?: Rule[];
53
58
  itemRender?: ItemRender;
59
+ tooltipConfig?: TooltipConfig;
54
60
  slots?: {
55
61
  default?: (
56
62
  { data, field }: ItemFuncArg<F>,
@@ -126,6 +132,8 @@ export interface PButtonProps extends ButtonProps {
126
132
 
127
133
  export interface ToolbarButtonProps extends PButtonProps {
128
134
  code: string;
135
+ dropdowns?: ToolbarButtonProps[];
136
+ size?: 'small' | 'large' | 'middle';
129
137
  }
130
138
 
131
139
  export interface ToolbarConfig {
@@ -163,16 +171,17 @@ export interface PageConfig {
163
171
  pageSize?: number;
164
172
  }
165
173
 
166
- export interface SelectConfig {
174
+ export interface SelectConfig<D = Recordable> {
167
175
  multiple?: boolean;
176
+ getCheckboxProps?: (record: D) => { disabled?: boolean };
168
177
  }
169
178
 
170
179
  export type PGridProps<D = Recordable, F = Recordable> = {
171
- selectConfig?: SelectConfig;
180
+ selectConfig?: SelectConfig<D>;
172
181
  rowKey?: string;
173
182
  manualFetch?: boolean;
174
183
  formConfig?: PFormProps<F>;
175
- columns: ColumnProps<D>[];
184
+ columns?: ColumnProps<D>[];
176
185
  toolbarConfig?: ToolbarConfig;
177
186
  pageConfig?: PageConfig;
178
187
  proxyConfig?: ProxyConfig<D, F>;
@@ -242,11 +251,12 @@ export interface PGridInstance<D = Recordable, F = Recordable> {
242
251
  query: () => Promise<D[]>;
243
252
  reload: () => Promise<D[]>;
244
253
  reloadPage: () => Promise<D[]>;
245
- passQuery: (query: Partial<F>) => Promise<D[]>;
254
+ passQuery: (query: Partial<F>, lazy?: boolean) => Promise<void | D[]>;
246
255
  };
247
256
  selectedRowKeys: string[] | number[];
248
257
  $table: Recordable;
249
258
  $form: Recordable;
259
+ setLoadings: (value: boolean) => void;
250
260
  }
251
261
 
252
262
  export interface PFormInstance {
@@ -259,7 +269,7 @@ export interface PromisePickerInstance<D = Recordable> {
259
269
  }
260
270
 
261
271
  export interface PFormBlockInstance {
262
- $form: Recordable
272
+ $form: Recordable;
263
273
  }
264
274
 
265
275
  export interface PFormGroupInstance<F = Recordable> {
@@ -1,106 +1,138 @@
1
1
  <script generic="F = Recordable" lang="ts" name="PForm" setup>
2
- import { PFormItemProps, PFormProps } from '#/antProxy'
3
- import { computed, onMounted, ref, toRefs, watch } from 'vue'
4
- import { debounce, omit } from 'lodash-es'
5
- import { v4 as uuid_v4 } from 'uuid'
6
- import RenderAntItem from '@/components/RenderAntItem'
7
- import RenderItemSlots from '@/components/RenderItemSlots'
8
- import { valued } from '@/utils/is'
9
- import { eachTree } from '@/utils/treeHelper'
10
- import { defaultItemResponsive } from '@/utils/core'
11
- import { Form as AForm, Row as ARow, Col as ACol, FormItem as AFormItem } from 'ant-design-vue'
2
+ import { PFormItemProps, PFormProps } from '#/antProxy';
3
+ import { computed, onMounted, ref, toRefs, watch } from 'vue';
4
+ import { debounce, omit, isFunction } from 'lodash-es';
5
+ import { v4 as uuid_v4 } from 'uuid';
6
+ import RenderAntItem from '@/components/RenderAntItem';
7
+ import RenderItemSlots from '@/components/RenderItemSlots';
8
+ import { valued } from '@/utils/is';
9
+ import { eachTree } from '@/utils/treeHelper';
10
+ import { defaultItemResponsive } from '@/utils/core';
11
+ import {
12
+ Form as AForm,
13
+ Row as ARow,
14
+ Col as ACol,
15
+ FormItem as AFormItem,
16
+ Tooltip as ATooltip,
17
+ } from 'ant-design-vue';
18
+ import { InfoCircleOutlined } from '@ant-design/icons-vue';
12
19
 
13
- const props = defineProps<PFormProps<F> & { data: F }>()
14
- const emit = defineEmits(['apply', 'reset'])
15
- const { items, data: formData } = toRefs(props)
20
+ const props = defineProps<PFormProps<F> & { data: F }>();
21
+ const emit = defineEmits(['apply', 'reset']);
22
+ const { items, data: formData } = toRefs(props);
16
23
 
17
- function handleSubmit () {
18
- emit('apply', formData.value)
19
- }
24
+ function handleSubmit() {
25
+ emit('apply', formData.value);
26
+ }
20
27
 
21
- const formEl = ref()
22
- const renderFormKey = ref(uuid_v4())
23
- const resetForm = () => {
24
- renderFormKey.value = uuid_v4()
25
- }
26
- const debounceResetForm = debounce(resetForm, 100)
27
- watch(() => items.value, debounceResetForm, { deep: true })
28
- const resetFormData = () => {
29
- if (props.customReset) {
30
- props.customReset()
31
- } else {
32
- const obj: Partial<F> = {}
33
- eachTree(items.value, (item) => {
34
- if (item.field && item.itemRender) {
35
- if (valued(item.itemRender.defaultValue)) {
36
- obj[item.field] = item.itemRender.defaultValue
37
- } else {
38
- obj[item.field] = undefined
28
+ const formEl = ref();
29
+ const renderFormKey = ref(uuid_v4());
30
+ const resetForm = () => {
31
+ renderFormKey.value = uuid_v4();
32
+ };
33
+ const debounceResetForm = debounce(resetForm, 100);
34
+ watch(() => items.value, debounceResetForm, { deep: true });
35
+ const resetFormData = () => {
36
+ if (props.customReset) {
37
+ props.customReset();
38
+ } else {
39
+ const obj: Partial<F> = {};
40
+ eachTree(items.value, (item) => {
41
+ if (item.field && item.itemRender) {
42
+ if (valued(item.itemRender.defaultValue)) {
43
+ obj[item.field] = item.itemRender.defaultValue;
44
+ } else {
45
+ obj[item.field] = undefined;
46
+ }
39
47
  }
40
- }
41
- })
42
- Object.assign(formData.value, obj)
43
- }
44
- }
45
- // omit({labelCol:defaultLabelCol,...props},['items','data','model'])
46
- const fc = computed(() => ({
47
- ...omit(props, ['items', 'data', 'model', 'labelCol', 'wrapperCol']),
48
- labelCol: props.labelCol ?? { span: 6 },
49
- wrapperCol: props.wrapperCol ?? { span: 16 },
50
- }))
51
- const validateField = (fields?: string | string[]) => {
52
- if (fields) {
53
- formEl.value?.validateFields(fields)
54
- }
55
- }
56
- const handleTrigger = (cusFields?: string | string[]) => {
57
- validateField(cusFields)
58
- }
59
- defineExpose({
60
- reset: resetForm,
61
- $form: computed(() => formEl.value),
62
- })
63
- onMounted(() => {
64
- resetForm()
65
- })
48
+ });
49
+ Object.assign(formData.value, obj);
50
+ }
51
+ };
52
+ // omit({labelCol:defaultLabelCol,...props},['items','data','model'])
53
+ const fc = computed(() => ({
54
+ ...omit(props, ['items', 'data', 'model', 'labelCol', 'wrapperCol']),
55
+ labelCol: props.labelCol ?? { span: 6 },
56
+ wrapperCol: props.wrapperCol ?? { span: 16 },
57
+ }));
58
+ const validateField = (fields?: string | string[]) => {
59
+ if (fields) {
60
+ formEl.value?.validateFields(fields);
61
+ }
62
+ };
63
+ const handleTrigger = (cusFields?: string | string[]) => {
64
+ validateField(cusFields);
65
+ };
66
+ defineExpose({
67
+ reset: resetForm,
68
+ $form: computed(() => formEl.value),
69
+ });
70
+ onMounted(() => {
71
+ resetForm();
72
+ });
66
73
  </script>
67
74
  <template>
68
75
  <div
69
- v-if="items.some((s: PFormItemProps<F>) => s.field && s.itemRender)"
70
- class="p-pane p-wrapper"
76
+ v-if="items.some((s: PFormItemProps<F>) => s.field && s.itemRender)"
77
+ class="p-pane p-wrapper"
71
78
  >
72
79
  <a-form :key="renderFormKey" ref="formEl" :model="formData" v-bind="fc" @submit="handleSubmit">
73
80
  <a-row :gutter="[6, 12]">
74
81
  <a-col
75
- v-for="(item, idx) in items"
76
- :key="'_col_' + idx"
77
- v-bind="item.col ?? (item.span ? { span: item.span } : defaultItemResponsive)"
82
+ v-for="(item, idx) in items"
83
+ :key="'_col_' + idx"
84
+ v-bind="item.col ?? (item.span ? { span: item.span } : defaultItemResponsive)"
78
85
  >
79
86
  <a-form-item
80
- :key="'_item_' + idx"
81
- :label="item.title"
82
- :name="item.field"
83
- :wrapper-col="item.wrapperCol ?? (item.title ? undefined : { span: 24 })"
84
- colon
85
- :class="`p-content-align-${item.align ?? 'left'}`"
86
- v-bind="omit(item, ['field', 'title', 'span', 'col', 'wrapperCol', 'itemRender'])"
87
+ :key="'_item_' + idx"
88
+ :label="item.title"
89
+ :name="item.field"
90
+ :wrapper-col="item.wrapperCol ?? (item.title ? undefined : { span: 24 })"
91
+ colon
92
+ :class="`p-content-align-${item.align ?? 'left'} ${item.forceRequired ? 'p-required' : ''}`"
93
+ v-bind="
94
+ omit(item, [
95
+ 'field',
96
+ 'title',
97
+ 'span',
98
+ 'col',
99
+ 'wrapperCol',
100
+ 'itemRender',
101
+ 'forceRequired',
102
+ 'tooltip',
103
+ ])
104
+ "
87
105
  >
88
106
  <render-item-slots
89
- v-if="item.slots?.default"
90
- :form-data="formData"
91
- :item="item"
92
- :key="'_sl_'+(item.field??'_')+'_'+idx"
93
- :pass-trigger="handleTrigger"
107
+ v-if="item.slots?.default"
108
+ :form-data="formData"
109
+ :item="item"
110
+ :key="'_sl_' + (item.field ?? '_') + '_' + idx"
111
+ :pass-trigger="handleTrigger"
94
112
  />
95
113
  <render-ant-item
96
- v-else-if="item.itemRender?.name"
97
- :key="'_re_'+(item.field??'_')+'_'+idx"
98
- :default-handler="{ reset: resetFormData }"
99
- :item-render="item.itemRender"
100
- @trigger="handleTrigger"
101
- :render-form-params="{ data: formData, field: item.field }"
114
+ v-else-if="item.itemRender?.name"
115
+ :key="'_re_' + (item.field ?? '_') + '_' + idx"
116
+ :default-handler="{ reset: resetFormData }"
117
+ :item-render="item.itemRender"
118
+ @trigger="handleTrigger"
119
+ :render-form-params="{ data: formData, field: item.field }"
102
120
  />
103
121
  <span v-else></span>
122
+ <template #tooltip v-if="item.tooltipConfig">
123
+ <a-tooltip
124
+ v-if="isFunction(item.tooltipConfig.title)"
125
+ v-bind="omit(item.tooltipConfig, ['title'])"
126
+ >
127
+ <InfoCircleOutlined class="cursor-pointer py-4x px-2x" />
128
+ <template #title>
129
+ <div v-html="item.tooltipConfig.title()"></div>
130
+ </template>
131
+ </a-tooltip>
132
+ <a-tooltip v-else v-bind="item.tooltipConfig">
133
+ <InfoCircleOutlined class="cursor-pointer py-4x px-2x" />
134
+ </a-tooltip>
135
+ </template>
104
136
  </a-form-item>
105
137
  </a-col>
106
138
  </a-row>
@@ -1,61 +1,67 @@
1
1
  <script lang="ts" generic="F = Recordable" setup name="PFormGroup">
2
- import { computed, PropType, ref } from 'vue'
3
- import { PFormGroupProps, PFormBlockInstance } from '#/antProxy'
4
- import { MoreOutlined } from '@ant-design/icons-vue'
5
- import { cloneDeep, toString, isFunction, omit } from 'lodash-es'
6
- import PGroupBlock from '@/components/PGroupBlock.vue'
7
- import {
8
- Card as ACard,
9
- Tabs as ATabs,
10
- TabPane as ATabPane,
11
- Button as AButton,
12
- Dropdown as ADropdown,
13
- Menu as AMenu,
14
- MenuItem as AMenuItem,
15
- } from 'ant-design-vue'
2
+ import { computed, nextTick, PropType, ref } from 'vue';
3
+ import { PFormGroupProps, PFormBlockInstance } from '#/antProxy';
4
+ import { MoreOutlined } from '@ant-design/icons-vue';
5
+ import { cloneDeep, toString, isFunction, omit } from 'lodash-es';
6
+ import PGroupBlock from '@/components/PGroupBlock.vue';
7
+ import {
8
+ Card as ACard,
9
+ Tabs as ATabs,
10
+ TabPane as ATabPane,
11
+ Button as AButton,
12
+ Dropdown as ADropdown,
13
+ Menu as AMenu,
14
+ MenuItem as AMenuItem,
15
+ } from 'ant-design-vue';
16
16
 
17
- const props = defineProps<PFormGroupProps<F>>()
18
- const model = defineModel({
19
- type: Array as PropType<Partial<F>[]>,
20
- default: () => [],
21
- })
22
- const activeKey = ref(0)
23
- const blockInstance = ref<PFormBlockInstance[]>([])
24
- const setActiveKey = (key: number) => {
25
- activeKey.value = key
26
- }
27
- const maxLen = props.max ?? Infinity
28
- const handleAddItem = (idx: number) => {
29
- const creator = props.creatItem ?? (() => Promise.resolve({} as Partial<F>))
30
- creator({ list: model.value }).then((item) => {
31
- model.value = [...model.value, item]
32
- activeKey.value = idx
33
- })
34
- }
35
- const itemMenus = props.itemMenus ?? [{ content: '删除', code: 'delete' }]
36
- const handleMenu = (key: string, item: Partial<F>, idx: number) => {
37
- if (props.menuHandler && isFunction(props.menuHandler)) {
38
- props.menuHandler({ code: key, data: item, index: idx })
39
- } else {
40
- switch (key) {
41
- case 'delete':
42
- model.value = model.value.filter((_, i) => i !== idx)
43
- break
44
- case 'copy':
45
- model.value = [...model.value, cloneDeep(omit(item, ['id']))]
46
- break
17
+ const props = defineProps<PFormGroupProps<F>>();
18
+ const model = defineModel({
19
+ type: Array as PropType<Partial<F>[]>,
20
+ default: () => [],
21
+ });
22
+ const activeKey = ref(0);
23
+ const blockInstance = ref<PFormBlockInstance[]>([]);
24
+ const setActiveKey = (key: number) => {
25
+ activeKey.value = key;
26
+ };
27
+ const maxLen = props.max ?? Infinity;
28
+ const handleAddItem = (idx: number) => {
29
+ const creator = props.creatItem ?? (() => Promise.resolve({} as Partial<F>));
30
+ creator({ list: model.value }).then((item) => {
31
+ model.value = [...model.value, item];
32
+ activeKey.value = idx;
33
+ });
34
+ };
35
+ const itemMenus = props.itemMenus ?? [{ content: '删除', code: 'delete' }];
36
+ const handleMenu = (key: string, item: Partial<F>, idx: number) => {
37
+ if (props.menuHandler && isFunction(props.menuHandler)) {
38
+ props.menuHandler({ code: key, data: item, index: idx });
39
+ } else {
40
+ switch (key) {
41
+ case 'delete':
42
+ model.value = model.value.filter((_, i) => i !== idx);
43
+ nextTick(() => {
44
+ blockInstance.value = blockInstance.value.filter((f) => !!f);
45
+ if (idx <= activeKey.value && activeKey.value > 0) {
46
+ activeKey.value--;
47
+ }
48
+ });
49
+ break;
50
+ case 'copy':
51
+ model.value = [...model.value, cloneDeep(omit(item, ['id']))];
52
+ break;
53
+ }
47
54
  }
48
- }
49
- }
50
- defineExpose({
51
- activeKey: computed(() => activeKey.value),
52
- setActiveKey,
53
- validateAll: () => {
54
- return Promise.all(blockInstance.value.map((block) =>
55
- block.$form?.validate() ?? Promise.resolve()
56
- ))
57
- },
58
- })
55
+ };
56
+ defineExpose({
57
+ activeKey: computed(() => activeKey.value),
58
+ setActiveKey,
59
+ validateAll: () => {
60
+ return Promise.all(
61
+ blockInstance.value.map((block) => block.$form?.validate() ?? Promise.resolve()),
62
+ );
63
+ },
64
+ });
59
65
  </script>
60
66
  <template>
61
67
  <a-card :title="title" size="small">
@@ -63,25 +69,30 @@ defineExpose({
63
69
  <template #rightExtra>
64
70
  <slot name="rightExtra">
65
71
  <a-button
66
- type="link"
67
- @click="handleAddItem(model.length)"
68
- v-if="showAdd && model.length < maxLen"
72
+ type="link"
73
+ @click="handleAddItem(model.length)"
74
+ v-if="showAdd && model.length < maxLen"
69
75
  >
70
76
  + 新增
71
77
  </a-button>
72
78
  </slot>
73
79
  </template>
74
- <a-tab-pane v-for="(item, idx) in model" :key="idx" :tab="`${tabLabel} ${idx + 1}`" force-render>
80
+ <a-tab-pane
81
+ v-for="(item, idx) in model"
82
+ :key="idx"
83
+ :tab="`${tabLabel} ${idx + 1}`"
84
+ force-render
85
+ >
75
86
  <template #closeIcon>
76
87
  <a-dropdown v-if="editAble && itemMenus?.length">
77
- <MoreOutlined/>
88
+ <MoreOutlined />
78
89
  <template #overlay>
79
90
  <a-menu @click="(e) => handleMenu(toString(e.key), item, idx)">
80
91
  <a-menu-item
81
- v-for="menu in itemMenus.filter(
92
+ v-for="menu in itemMenus.filter(
82
93
  (f) => f.visibleMethod?.({ data: item, index: idx }) ?? true,
83
94
  )"
84
- :key="menu.code"
95
+ :key="menu.code"
85
96
  >
86
97
  <div>{{ menu.content }}</div>
87
98
  </a-menu-item>
@@ -90,13 +101,20 @@ defineExpose({
90
101
  </a-dropdown>
91
102
  </template>
92
103
  <!-- @vue-ignore -->
93
- <p-group-block :ref="(i:PFormBlockInstance)=>{
94
- if(i){
95
- blockInstance[idx] = i
96
- }else if(blockInstance){
97
- blockInstance = blockInstance.filter((_,i)=>i!==idx)
98
- }
99
- }" :key="idx" :source="item" :get-form-setting="getFormSetting"/>
104
+ <p-group-block
105
+ :ref="
106
+ (i: PFormBlockInstance) => {
107
+ if (i) {
108
+ blockInstance[idx] = i;
109
+ } else if (blockInstance) {
110
+ blockInstance = blockInstance.filter((_, i) => i !== idx);
111
+ }
112
+ }
113
+ "
114
+ :key="idx"
115
+ :source="item"
116
+ :get-form-setting="getFormSetting"
117
+ />
100
118
  </a-tab-pane>
101
119
  </a-tabs>
102
120
  </a-card>