@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.
- package/dist/cjs/index.js +1 -1
- package/dist/declarations/antProxy.d.ts +16 -5
- package/dist/es/index.js +1136 -1009
- package/dist/packages/components/PGrid.vue.d.ts +2 -1
- package/dist/style.css +1 -1
- package/package.json +1 -2
- package/src/declarations/antProxy.ts +22 -12
- package/src/packages/components/PForm.vue +115 -83
- package/src/packages/components/PFormGroup.vue +87 -69
- package/src/packages/components/PGrid.vue +425 -364
- package/src/packages/store/renderStore.tsx +110 -104
- package/src/packages/styles/index.scss +32 -0
|
@@ -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
|
|
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.
|
|
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
|
|
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
|
|
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 {
|
|
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>
|