@vue-ui-kit/ant 1.1.2 → 1.3.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/dist/cjs/index.js +1 -1
- package/dist/declarations/antProxy.d.ts +6 -1
- package/dist/es/index.js +1010 -927
- package/dist/style.css +1 -1
- package/package.json +1 -2
- package/src/declarations/antProxy.ts +14 -8
- package/src/packages/components/PForm.vue +115 -83
- package/src/packages/components/PFormGroup.vue +87 -69
- package/src/packages/components/PGrid.vue +392 -361
- package/src/packages/store/renderStore.tsx +110 -104
- package/src/packages/styles/index.scss +32 -0
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.
|
|
3
|
+
"version": "1.3.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>,
|
|
@@ -259,7 +265,7 @@ export interface PromisePickerInstance<D = Recordable> {
|
|
|
259
265
|
}
|
|
260
266
|
|
|
261
267
|
export interface PFormBlockInstance {
|
|
262
|
-
$form: Recordable
|
|
268
|
+
$form: Recordable;
|
|
263
269
|
}
|
|
264
270
|
|
|
265
271
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
25
|
-
}
|
|
26
|
-
const debounceResetForm = debounce(resetForm, 100)
|
|
27
|
-
watch(() => items.value, debounceResetForm, { deep: true })
|
|
28
|
-
const resetFormData = () => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
})
|
|
22
|
-
const activeKey = ref(0)
|
|
23
|
-
const blockInstance = ref<PFormBlockInstance[]>([])
|
|
24
|
-
const setActiveKey = (key: number) => {
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
const maxLen = props.max ?? Infinity
|
|
28
|
-
const handleAddItem = (idx: number) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
const itemMenus = props.itemMenus ?? [{ content: '删除', code: 'delete' }]
|
|
36
|
-
const handleMenu = (key: string, item: Partial<F>, idx: number) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
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
|
-
|
|
92
|
+
v-for="menu in itemMenus.filter(
|
|
82
93
|
(f) => f.visibleMethod?.({ data: item, index: idx }) ?? true,
|
|
83
94
|
)"
|
|
84
|
-
|
|
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
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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>
|