@vue-ui-kit/ant 1.7.4 → 1.8.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.
@@ -0,0 +1,31 @@
1
+ import { PFormItemProps } from '../../declarations/antProxy';
2
+ declare const _default: <T extends Recordable = 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
+ props: __VLS_Prettify<Pick<Partial<{}> & Omit<{
4
+ readonly onTrigger?: ((cusFields?: string | string[] | undefined) => any) | undefined;
5
+ readonly onReset?: (() => any) | undefined;
6
+ readonly onDelayTrigger?: ((cusFields?: string | string[] | undefined, time?: number | undefined) => any) | undefined;
7
+ } & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps & Readonly<{}> & Readonly<{
8
+ onTrigger?: ((cusFields?: string | string[] | undefined) => any) | undefined;
9
+ onReset?: (() => any) | undefined;
10
+ onDelayTrigger?: ((cusFields?: string | string[] | undefined, time?: number | undefined) => any) | undefined;
11
+ }>, never>, "onReset" | "onTrigger" | "onDelayTrigger"> & {
12
+ item: PFormItemProps<T>;
13
+ formData: T;
14
+ }> & import('vue').PublicProps;
15
+ expose(exposed: import('vue').ShallowUnwrapRef<{}>): void;
16
+ attrs: any;
17
+ slots: ReturnType<() => {}>;
18
+ emit: {
19
+ (e: "delayTrigger", cusFields?: string | string[], time?: number): void;
20
+ (e: "trigger", cusFields?: string | string[]): void;
21
+ (e: "reset"): void;
22
+ } & {};
23
+ }>) => import('vue').VNode<import('vue').RendererNode, import('vue').RendererElement, {
24
+ [key: string]: any;
25
+ }> & {
26
+ __ctx?: Awaited<typeof __VLS_setup>;
27
+ };
28
+ export default _default;
29
+ type __VLS_Prettify<T> = {
30
+ [K in keyof T]: T[K];
31
+ } & {};
@@ -10,8 +10,8 @@ declare const _default: <F = Recordable<any>>(__VLS_props: NonNullable<Awaited<t
10
10
  expose(exposed: import('vue').ShallowUnwrapRef<{
11
11
  activeKey: import('vue').ComputedRef<number>;
12
12
  setActiveKey: (key: number) => void;
13
- validateAll: () => Promise<any[]>;
14
- validate: (__index: number) => any;
13
+ validateAll: () => Promise<PromiseSettledResult<any>[]>;
14
+ validate: (__index: number) => Promise<void>;
15
15
  }>): void;
16
16
  attrs: any;
17
17
  slots: ReturnType<() => {
@@ -39,16 +39,17 @@ declare const _default: <D = Recordable<any>, F = Recordable<any>>(__VLS_props:
39
39
  }>, never>, "onPick" | "onToolbarButtonClick" | "onToolbarToolClick" | "onResetQuery"> & PGridProps<D, F>> & import('vue').PublicProps;
40
40
  expose(exposed: import('vue').ShallowUnwrapRef<{
41
41
  commitProxy: {
42
- query: import('lodash').DebouncedFunc<() => Promise<D[]>>;
42
+ query: (this: any, ...args: any[]) => any;
43
43
  reload: () => void;
44
44
  reloadPage: () => Promise<D[]>;
45
- passQuery: (params: Partial<F>, lazy?: boolean) => Promise<void> | Promise<D[]> | undefined;
45
+ passQuery: (params: Partial<F>, lazy?: boolean) => any;
46
46
  };
47
47
  $table: import('vue').ComputedRef<any>;
48
48
  selectedRowKeys: import('vue').ComputedRef<(string | number)[]>;
49
49
  setBtnLoading: (code: string, value: boolean) => void;
50
50
  selectedRecords: import('vue').ComputedRef<D[]>;
51
51
  $form: import('vue').ComputedRef<any>;
52
+ getFormData: () => Partial<F>;
52
53
  setLoadings: (value: boolean | Record<string, boolean>) => void;
53
54
  resizeTable: () => void;
54
55
  }>): void;
@@ -5,4 +5,4 @@ export declare const defaultItemResponsive: Responsive;
5
5
  export declare const defaultLabelCol: Responsive;
6
6
  export declare const labelColDict: Record<number, Responsive>;
7
7
  export declare const get24rest: (exist: number[]) => number;
8
- export declare const getButtonResponsive: (itemResponsive: number | Responsive[]) => import('lodash').Dictionary<number>;
8
+ export declare const getButtonResponsive: (itemResponsive: number | Responsive[]) => any;
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%}.cursor-pointer{cursor:pointer}.py-4x{padding-top:4px;padding-bottom:4px}.px-2x{padding-left:2px;padding-right:2px}.pl-4x{padding-left:1em}.pl-8{padding-left:2em}.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}
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}.pl-4x{padding-left:1em}.pl-8{padding-left:2em}.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-error-group-tab{position:relative;overflow:visible;color:#f5222d!important}.p-error-group-tab:after{content:"🏷️";position:absolute;top:-1em;right:0;width:1em;height:1em;font-size:.8em;animation:heartbeat 2s infinite}.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}@keyframes heartbeat{0%{transform:scale(1)}6%{transform:scale(1.22)}12%{transform:scale(1)}18%{transform:scale(1.18)}24%{transform:scale(1)}48%{transform:scale(1)}54%{transform:scale(1.22)}60%{transform:scale(1)}66%{transform:scale(1.18)}72%{transform:scale(1)}to{transform:scale(1)}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vue-ui-kit/ant",
3
- "version": "1.7.4",
3
+ "version": "1.8.0",
4
4
  "description": "Vue3 UI Kit based on Ant Design",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -39,8 +39,8 @@
39
39
  "email": "adoin@qq.com"
40
40
  },
41
41
  "dependencies": {
42
- "lodash-es": "^4.17.21",
43
- "uuid": "^10.0.0"
42
+ "uuid": "^10.0.0",
43
+ "xe-utils": "^3.7.6"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "@ant-design/icons-vue": ">=7.0.0",
@@ -55,7 +55,6 @@
55
55
  "@ant-design/icons-vue": ">=7.0.0",
56
56
  "@commitlint/cli": "^19.4.1",
57
57
  "@commitlint/config-conventional": "^19.4.1",
58
- "@types/lodash-es": "^4.17.12",
59
58
  "@types/uuid": "^10.0.0",
60
59
  "@vitejs/plugin-vue": "^5.1.2",
61
60
  "@vitejs/plugin-vue-jsx": "^4.0.1",
@@ -54,6 +54,8 @@ export interface PFormItemProps<F = Recordable> {
54
54
  title?: string;
55
55
  span?: number;
56
56
  colon?: boolean;
57
+ labelCol?: ColProps
58
+ wrapperCol?: ColProps
57
59
  forceRequired?: boolean; // 在页面需要必填标示,但field不需要必填校验时使用
58
60
  align?: 'left' | 'right' | 'center';
59
61
  col?: ColProps;
@@ -67,6 +69,7 @@ export interface PFormItemProps<F = Recordable> {
67
69
  passTrigger?: (cusFields?: string | string[]) => void,
68
70
  passDelayTrigger?: (cusFields?: string | string[], time?: number) => void
69
71
  ) => any;
72
+ defaultValue?: any
70
73
  };
71
74
  }
72
75
 
@@ -109,12 +112,13 @@ export interface PFormGroupProps<F = Recordable> {
109
112
  tabLabel?: string;
110
113
  editAble?: boolean;
111
114
  showAdd?: boolean;
115
+ lazyErrorMark?: boolean;
112
116
  forceRender?: boolean;
113
117
  /* 是否保持连续编号 */
114
118
  keepSerial?: boolean;
115
119
  loading?: boolean;
116
120
  itemMenus?: Array<GroupMenuItem<F>>;
117
- creatItem?: ({ list }: { list?: Partial<F>[] }) => Promise<Partial<F>>;
121
+ createItem?: ({ list }: { list?: Partial<F>[] }) => Promise<Partial<F>>;
118
122
  max?: number;
119
123
  menuHandler?: GroupMenuItemHandler<F>;
120
124
  }
@@ -275,6 +279,7 @@ export interface PGridInstance<D = Recordable, F = Recordable> {
275
279
  selectedRecords: D[];
276
280
  $table: Recordable;
277
281
  $form: Recordable;
282
+ getFormData: () => Partial<F>;
278
283
  setLoadings: (value: boolean) => void;
279
284
  setBtnLoading: (code: string, status: boolean) => void;
280
285
  resizeTable: () => void;
@@ -1,21 +1,15 @@
1
1
  <script generic="F = Recordable" lang="ts" name="PForm" setup>
2
2
  import { PFormItemProps, PFormProps } from '#/antProxy';
3
- import { computed, onMounted, ref, toRefs, watch } from 'vue';
4
- import { debounce, omit, isFunction } from 'lodash-es';
3
+ import { computed, ref, toRefs } from 'vue';
4
+ import { omit } from 'xe-utils';
5
5
  import { v4 as uuid_v4 } from 'uuid';
6
- import RenderAntItem from '@/components/RenderAntItem';
7
- import RenderItemSlots from '@/components/RenderItemSlots';
8
- import { isGoodValue, valued } from '@/utils/is';
6
+ import { isGoodValue } from '@/utils/is';
9
7
  import { eachTree } from '@/utils/treeHelper';
10
- import { defaultItemResponsive } from '@/utils/core';
8
+ import PFormCol from '@/components/PFormCol.vue';
11
9
  import {
12
10
  Form as AForm,
13
11
  Row as ARow,
14
- Col as ACol,
15
- FormItem as AFormItem,
16
- Tooltip as ATooltip,
17
12
  } from 'ant-design-vue';
18
- import { InfoCircleOutlined } from '@ant-design/icons-vue';
19
13
 
20
14
  const props = defineProps<PFormProps<F> & { data: F }>();
21
15
  const emit = defineEmits(['apply', 'reset']);
@@ -30,8 +24,6 @@
30
24
  const resetForm = () => {
31
25
  renderFormKey.value = uuid_v4();
32
26
  };
33
- const debounceResetForm = debounce(resetForm, 100);
34
- watch(() => items.value, debounceResetForm, { deep: true });
35
27
  const resetFormData = () => {
36
28
  if (props.customReset) {
37
29
  props.customReset();
@@ -45,9 +37,18 @@
45
37
  obj[item.field] = undefined;
46
38
  }
47
39
  }
40
+ else if (item.field && item.slots) {
41
+ if (isGoodValue(item.slots.defaultValue)) {
42
+ obj[item.field] = item.slots.defaultValue
43
+ }
44
+ else {
45
+ obj[item.field] = undefined;
46
+ }
47
+ }
48
48
  });
49
49
  Object.assign(formData.value, obj);
50
50
  }
51
+ emit('reset');
51
52
  };
52
53
  // omit({labelCol:defaultLabelCol,...props},['items','data','model'])
53
54
  const fc = computed(() => ({
@@ -73,9 +74,6 @@
73
74
  reset: resetForm,
74
75
  $form: computed(() => formEl.value),
75
76
  });
76
- onMounted(() => {
77
- resetForm();
78
- });
79
77
  </script>
80
78
  <template>
81
79
  <div
@@ -84,68 +82,15 @@
84
82
  >
85
83
  <a-form :key="renderFormKey" ref="formEl" :model="formData" v-bind="fc" @submit="handleSubmit">
86
84
  <a-row :gutter="[6, 12]">
87
- <a-col
85
+ <p-form-col
88
86
  v-for="(item, idx) in items"
89
- :key="'_col_' + idx"
90
- v-bind="item.col ?? (item.span ? { span: item.span } : defaultItemResponsive)"
91
- >
92
- <a-form-item
93
- :key="'_item_' + idx"
94
- :label="item.title"
95
- :name="item.field"
96
- :wrapper-col="item.wrapperCol ?? (item.title ? undefined : { span: 24 })"
97
- colon
98
- :class="`p-content-align-${item.align ?? 'left'} ${item.forceRequired ? 'p-required' : ''}`"
99
- v-bind="
100
- omit(item, [
101
- 'field',
102
- 'title',
103
- 'span',
104
- 'col',
105
- 'wrapperCol',
106
- 'itemRender',
107
- 'forceRequired',
108
- 'tooltip',
109
- ])
110
- "
111
- >
112
- <template v-if="item.slots?.title" #label>
113
- <component :is="item.slots.title" />
114
- </template>
115
- <render-item-slots
116
- v-if="item.slots?.default"
117
- :form-data="formData"
118
- :item="item"
119
- :key="'_sl_' + (item.field ?? '_') + '_' + idx"
120
- :pass-trigger="handleTrigger"
121
- :pass-delay-trigger="handleDelayTrigger"
122
- />
123
- <render-ant-item
124
- v-else-if="item.itemRender?.name"
125
- :key="'_re_' + (item.field ?? '_') + '_' + idx"
126
- :default-handler="{ reset: resetFormData }"
127
- :item-render="item.itemRender"
128
- @trigger="handleTrigger"
129
- @delay-trigger="handleDelayTrigger"
130
- :render-form-params="{ data: formData, field: item.field }"
131
- />
132
- <span v-else></span>
133
- <template #tooltip v-if="item.tooltipConfig">
134
- <a-tooltip
135
- v-if="isFunction(item.tooltipConfig.title)"
136
- v-bind="omit(item.tooltipConfig, ['title'])"
137
- >
138
- <InfoCircleOutlined class="cursor-pointer py-4x px-2x" />
139
- <template #title>
140
- <div v-html="item.tooltipConfig.title()"></div>
141
- </template>
142
- </a-tooltip>
143
- <a-tooltip v-else v-bind="item.tooltipConfig">
144
- <InfoCircleOutlined class="cursor-pointer py-4x px-2x" />
145
- </a-tooltip>
146
- </template>
147
- </a-form-item>
148
- </a-col>
87
+ :key="`_col_${idx}`"
88
+ :form-data="formData"
89
+ :item="item"
90
+ @delay-trigger="handleDelayTrigger"
91
+ @reset="resetFormData"
92
+ @trigger="handleTrigger"
93
+ />
149
94
  </a-row>
150
95
  </a-form>
151
96
  </div>
@@ -0,0 +1,89 @@
1
+ <script setup lang="ts" generic="T extends Recordable = Recordable">
2
+ import type { PFormItemProps } from '#/antProxy'
3
+ import { defaultItemResponsive } from '@/utils/core';
4
+ import RenderAntItem from '@/components/RenderAntItem';
5
+ import RenderItemSlots from '@/components/RenderItemSlots';
6
+ import { InfoCircleOutlined } from '@ant-design/icons-vue'
7
+ import { v4 as uuid_v4 } from 'uuid'
8
+ import { ref, watch } from 'vue'
9
+ import { isEqual,debounce, isFunction, omit } from 'xe-utils'
10
+
11
+ const props = defineProps<{
12
+ item: PFormItemProps<T>
13
+ formData: T
14
+ }>()
15
+ const emit = defineEmits<{
16
+ (e: 'delayTrigger', cusFields?: string | string[], time?: number): void
17
+ (e: 'trigger', cusFields?: string | string[]): void
18
+ (e: 'reset'): void
19
+ }>()
20
+ const renderFormKey = ref(uuid_v4())
21
+ const handleDelayTrigger = (cusFields?: string | string[], time?: number) => {
22
+ emit('delayTrigger', cusFields, time)
23
+ }
24
+ const handleTrigger = (cusFields?: string | string[]) => {
25
+ emit('trigger', cusFields)
26
+ }
27
+ const resetFormData = () => {
28
+ emit('reset')
29
+ }
30
+ const refreshCol = () => {
31
+ renderFormKey.value = uuid_v4()
32
+ }
33
+ const debounceRefreshCol = debounce(refreshCol, 100)
34
+ watch(() => props.item, (cur, old) => {
35
+ if (!isEqual(cur, old)) {
36
+ debounceRefreshCol()
37
+ }
38
+ }, { deep: true })
39
+ </script>
40
+
41
+ <template>
42
+ <a-col
43
+ :key="`_col_${renderFormKey}`"
44
+ v-bind="item.col ?? (item.span ? { span: item.span } : defaultItemResponsive)"
45
+ >
46
+ <a-form-item
47
+ :key="`_item_${renderFormKey}`"
48
+ :class="`p-content-align-${item.align ?? 'left'} ${item.forceRequired ? 'p-required' : ''}`"
49
+ colon
50
+ :label="item.title"
51
+ :name="item.field"
52
+ :wrapper-col="item.wrapperCol ?? (item.title ? undefined : { span: 24 })"
53
+ v-bind="omit(item, ['field', 'title', 'span', 'col', 'wrapperCol', 'itemRender', 'forceRequired', 'tooltip'])"
54
+ >
55
+ <template v-if="item.slots?.title" #label>
56
+ <component :is="item.slots.title" />
57
+ </template>
58
+ <render-item-slots
59
+ v-if="item.slots?.default"
60
+ :key="`_sl_${item.field ?? '_'}_${renderFormKey}`"
61
+ :form-data="formData"
62
+ :item="item"
63
+ :pass-delay-trigger="handleDelayTrigger"
64
+ :pass-trigger="handleTrigger"
65
+ />
66
+ <render-ant-item
67
+ v-else-if="item.itemRender?.name"
68
+ :key="`_re_${item.field ?? '_'}_${renderFormKey}`"
69
+ :default-handler="{ reset: resetFormData }"
70
+ :item-render="item.itemRender"
71
+ :render-form-params="{ data: formData as Recordable, field: item.field }"
72
+ @delay-trigger="handleDelayTrigger"
73
+ @trigger="handleTrigger"
74
+ />
75
+ <span v-else />
76
+ <template v-if="item.tooltipConfig" #tooltip>
77
+ <a-tooltip v-if="isFunction(item.tooltipConfig.title)" v-bind="omit(item.tooltipConfig, ['title'])">
78
+ <info-circle-outlined class="cursor-pointer py-4 px-2" />
79
+ <template #title>
80
+ <div v-html="item.tooltipConfig.title()" />
81
+ </template>
82
+ </a-tooltip>
83
+ <a-tooltip v-else v-bind="item.tooltipConfig">
84
+ <info-circle-outlined class="cursor-pointer py-4 px-2" />
85
+ </a-tooltip>
86
+ </template>
87
+ </a-form-item>
88
+ </a-col>
89
+ </template>
@@ -1,9 +1,10 @@
1
1
  <script lang="ts" generic="F = Recordable" setup name="PFormGroup">
2
- import { computed, nextTick, PropType, ref, watchEffect } from 'vue';
2
+ import { computed, nextTick, PropType, ref, watch, watchEffect } from 'vue';
3
3
  import { PFormGroupProps, PFormBlockInstance } from '#/antProxy';
4
4
  import { MoreOutlined } from '@ant-design/icons-vue';
5
+ import type { Tabs } from 'ant-design-vue'
5
6
  import { Form } from 'ant-design-vue'
6
- import { cloneDeep, toString, isFunction, omit, maxBy, debounce } from 'lodash-es';
7
+ import { clone, toString, isFunction, omit, max, debounce } from 'xe-utils';
7
8
  import PGroupBlock from '@/components/PGroupBlock.vue';
8
9
  import {
9
10
  Card as ACard,
@@ -19,25 +20,28 @@
19
20
  import { $warning } from '@/hooks/useMessage';
20
21
 
21
22
  const useForm = Form.useForm
23
+ const tabsRef = ref<InstanceType<typeof Tabs>>()
22
24
  const props = defineProps<PFormGroupProps<F>>();
25
+ const rootRef = ref<InstanceType<typeof ACard>>()
23
26
  const model = defineModel({
24
27
  type: Array as PropType<Partial<F & { __index: number }>[]>,
25
28
  default: () => [],
26
29
  });
27
30
  const activeKey = ref(0);
31
+ const error_indexes = ref<number[]>([])
28
32
  const blockInstance = ref<PFormBlockInstance[]>([]);
29
33
  const setActiveKey = (key: number) => {
30
34
  activeKey.value = key;
31
35
  };
32
36
  const maxLen = computed(() => props.max ?? Infinity);
33
37
  const handleAddItem = (idx: number) => {
34
- const creator = props.creatItem ?? (() => Promise.resolve({} as Partial<F>));
38
+ const creator = props.createItem ?? (() => Promise.resolve({} as Partial<F>));
35
39
  creator({ list: model.value }).then((item) => {
36
40
  model.value = [
37
41
  ...model.value,
38
42
  {
39
43
  ...item,
40
- __index: (maxBy(model.value, (m) => m.__index ?? -1)?.__index ?? -1) + 1,
44
+ __index: (max(model.value, (m) => m.__index ?? -1)?.__index ?? -1) + 1,
41
45
  } as Partial<F & { __index: number }>,
42
46
  ];
43
47
  activeKey.value = idx;
@@ -47,26 +51,34 @@
47
51
  { content: '复制', code: 'copy' },
48
52
  { content: '删除', code: 'delete' },
49
53
  ];
54
+ const getPopupContainer = () => rootRef.value?.$el ?? document.body
50
55
  // 实际是否强制渲染
51
56
  const fr = computed(() => {
52
57
  return props.forceRender || model.value.length <= 5
53
58
  })
59
+ const handleBlockFocus = (idx: number) => {
60
+ const target_index = model.value.find((f, index) => index === idx)?.__index
61
+ if (valued(target_index)) {
62
+ error_indexes.value = error_indexes.value.filter(f => f !== target_index)
63
+ }
64
+ }
54
65
  const handleTabChange = () => {
55
66
  nextTick().then(() => {
67
+ handleBlockFocus(activeKey.value)
56
68
  blockInstance.value[activeKey.value]?.$form?.validate()
57
69
  })
58
70
  }
59
- watchEffect(() => {
60
- if (!props.keepSerial) {
61
- const unSortItems = model.value.filter((f) => !valued(f.__index));
62
- if (unSortItems.length > 0) {
63
- unSortItems.forEach((item) => {
64
- // @ts-ignore
65
- item.__index = (maxBy(model.value, (m) => m.__index ?? -1)?.__index ?? -1) + 1;
66
- });
67
- }
71
+ watch(() => model.value, () => {
72
+ if (!props.keepSerial) {
73
+ const unSortItems = model.value.filter(f => !valued(f.__index))
74
+ if (unSortItems.length > 0) {
75
+ unSortItems.forEach((item) => {
76
+ // @ts-ignore
77
+ item.__index = (max(model.value, m => m.__index ?? -1)?.__index ?? -1) + 1
78
+ })
68
79
  }
69
- });
80
+ }
81
+ }, { immediate: true })
70
82
  const handleMenu = ({ key }: MenuInfo, item: Partial<F & { __index: number }>, idx: number) => {
71
83
  if (props.menuHandler && isFunction(props.menuHandler)) {
72
84
  props.menuHandler({ code: toString(key), data: item, index: idx });
@@ -88,9 +100,9 @@
88
100
  }
89
101
  model.value = [
90
102
  ...model.value,
91
- cloneDeep({
103
+ clone({
92
104
  ...omit(item, ['id', '__index']),
93
- __index: (maxBy(model.value, (m) => m.__index ?? -1)?.__index ?? -1) + 1,
105
+ __index: (max(model.value, (m) => m.__index ?? -1)?.__index ?? -1) + 1,
94
106
  }),
95
107
  ] as Partial<F & { __index: number }>[];
96
108
  break;
@@ -105,26 +117,74 @@
105
117
  activeKey.value = model.value.length - 1;
106
118
  }
107
119
  });
120
+
121
+ const debounceHandleBlockFocus = debounce(handleBlockFocus, 50)
122
+ watchEffect(() => {
123
+ const errorKeys = model.value.map((m, idx) => error_indexes.value.includes(m.__index!) ? idx : undefined).filter(valued)
124
+ const rootDom = tabsRef.value?.$el
125
+ if (rootDom) {
126
+ const tabPanel = rootDom.querySelectorAll(':scope >.ant-tabs-nav>.ant-tabs-nav-wrap>.ant-tabs-nav-list>.ant-tabs-tab>.ant-tabs-tab-btn')
127
+ tabPanel.forEach((tab, idx) => {
128
+ if (errorKeys.includes(idx)) {
129
+ tab.classList.add('p-error-group-tab')
130
+ }
131
+ else {
132
+ tab.classList.remove('p-error-group-tab')
133
+ }
134
+ })
135
+ }
136
+ })
108
137
  defineExpose({
109
138
  activeKey: computed(() => activeKey.value),
110
139
  setActiveKey,
111
140
  validateAll: () => {
112
- return Promise.all(blockInstance.value.map((block, idx) =>
113
- fr.value ? (block.$form?.validate() ?? Promise.resolve()) : (useForm(model.value[idx], props.getFormSetting(model.value[idx]).rules)?.validate() ?? Promise.resolve()),
114
- ))
115
- },
116
- validate: (__index: number) => {
117
- const index = model.value.findIndex(f => f.__index === __index)
118
- return fr.value
141
+ const promiseList = fr.value
142
+ ? blockInstance.value.map(block => block.$form?.validate())
143
+ : model.value.map(m => useForm(m, props.getFormSetting(m).rules)?.validate() ?? Promise.resolve())
144
+ return Promise.allSettled(promiseList).then((results) => {
145
+ // 更新 error_indexes
146
+ error_indexes.value = results
147
+ .map((res, idx) => (res.status === 'rejected' && typeof model.value[idx]?.__index === 'number' ? model.value[idx].__index as number : undefined))
148
+ .filter((v): v is number => typeof v === 'number')
149
+ if (!props.lazyErrorMark && error_indexes.value.length) {
150
+ // 跳到第一个出错的tab
151
+ const firstErrorIndex = error_indexes.value[0]
152
+ const firstErrorTab = model.value.findIndex(f => f.__index === firstErrorIndex)
153
+ if (firstErrorTab !== -1) {
154
+ activeKey.value = firstErrorTab
155
+ }
156
+ }
157
+ // 判断是否有错误
158
+ if (error_indexes.value.length) {
159
+ return Promise.reject({ error_indexes: error_indexes.value, results })
160
+ }
161
+ return Promise.resolve(results)
162
+ })
163
+ },
164
+ validate: async (__index: number) => {
165
+ const index = model.value.findIndex(f => f.__index === __index)
166
+ try {
167
+ await (fr.value
119
168
  ? (blockInstance.value[index]?.$form?.validate() ?? Promise.resolve())
120
- : (useForm(model.value[index], props.getFormSetting(model.value[index]).rules)?.validate() ?? Promise.resolve())
121
- },
169
+ : (useForm(model.value[index], props.getFormSetting(model.value[index]).rules)?.validate() ?? Promise.resolve()))
170
+ // 校验通过,移除error_indexes中的__index
171
+ error_indexes.value = error_indexes.value.filter(f => f !== __index)
172
+ return Promise.resolve()
173
+ }
174
+ catch (e) {
175
+ // 校验失败,加入error_indexes
176
+ if (!error_indexes.value.includes(__index)) {
177
+ error_indexes.value = [...error_indexes.value, __index]
178
+ }
179
+ return Promise.reject(e)
180
+ }
181
+ },
122
182
  });
123
183
  </script>
124
184
  <template>
125
- <a-card :title="title" size="small">
185
+ <a-card ref="rootRef" :title="title" size="small">
126
186
  <a-spin v-if="loading" class="w-full" />
127
- <a-tabs v-else type="editable-card" v-model:activeKey="activeKey" hide-add @change="handleTabChange">
187
+ <a-tabs v-else ref="tabsRef" type="editable-card" v-model:activeKey="activeKey" hide-add @change="handleTabChange">
128
188
  <template #rightExtra>
129
189
  <slot name="rightExtra">
130
190
  <a-button
@@ -143,7 +203,7 @@
143
203
  :force-render="fr"
144
204
  >
145
205
  <template #closeIcon>
146
- <a-dropdown v-if="editAble && itemMenus?.length">
206
+ <a-dropdown v-if="editAble && itemMenus?.length" :get-popup-container="getPopupContainer">
147
207
  <MoreOutlined />
148
208
  <template #overlay>
149
209
  <a-menu @click="(e) => debounceHandleMenu(e, item, idx)">
@@ -173,6 +233,8 @@
173
233
  :key="idx"
174
234
  :source="item"
175
235
  :get-form-setting="getFormSetting"
236
+ @focus.capture="debounceHandleBlockFocus(idx)"
237
+ @click.capture="debounceHandleBlockFocus(idx)"
176
238
  />
177
239
  </a-tab-pane>
178
240
  </a-tabs>
@@ -11,7 +11,7 @@
11
11
  toRefs,
12
12
  onBeforeUnmount,
13
13
  } from 'vue';
14
- import { debounce, get, isArray, isBoolean, isFunction, isObject, isString, merge, omit, toNumber } from 'lodash-es';
14
+ import { debounce, get, isArray, isBoolean, isFunction, isObject, isString, merge, omit, toNumber } from 'xe-utils';
15
15
  import { eachTree } from '@/utils/treeHelper';
16
16
  import { message as $message } from 'ant-design-vue';
17
17
  import RenderAntItem from '@/components/RenderAntItem';
@@ -225,6 +225,14 @@
225
225
  obj[item.field] = undefined;
226
226
  }
227
227
  }
228
+ else if (item.field && item.slots) {
229
+ if (isGoodValue(item.slots.defaultValue)) {
230
+ obj[item.field] = item.slots.defaultValue
231
+ }
232
+ else {
233
+ obj[item.field] = undefined
234
+ }
235
+ }
228
236
  });
229
237
  queryFormData.value = obj;
230
238
  }
@@ -266,6 +274,8 @@
266
274
  };
267
275
  const enoughSpacing = ref(true);
268
276
  const reload = () => {
277
+ selectedCaches.value = []
278
+ selectedRowKeys.value = []
269
279
  return resetQueryFormData();
270
280
  };
271
281
  const resetPage = () => {
@@ -428,6 +438,7 @@
428
438
  setBtnLoading,
429
439
  selectedRecords,
430
440
  $form: computed(() => formEl.value),
441
+ getFormData: () => queryFormData.value,
431
442
  setLoadings,
432
443
  resizeTable,
433
444
  });
@@ -4,7 +4,7 @@
4
4
  import { ColumnProps } from '#/antProxy';
5
5
  import { cleanCol } from '@/utils/core';
6
6
  import RenderDefaultSlots from '@/components/RenderDefaultSlots';
7
- import { merge } from 'lodash-es';
7
+ import { merge } from 'xe-utils';
8
8
 
9
9
  const props = defineProps({
10
10
  modelValue: {
@@ -21,7 +21,7 @@ import {
21
21
  TimePicker,
22
22
  TreeSelect,
23
23
  } from 'ant-design-vue';
24
- import { set, isFunction, merge, omit } from 'lodash-es';
24
+ import { set, isFunction, merge, omit } from 'xe-utils';
25
25
  import { ButtonProps } from 'ant-design-vue/lib/button';
26
26
  import { isBadValue, isGoodValue, noValue, valued } from '@/utils/is';
27
27
  import TableInput from '@/renders/TableInput.vue';