瀏覽代碼

fix(form): form validate error

vben 4 年之前
父節點
當前提交
a305e59124

+ 2 - 1
CHANGELOG.zh_CN.md

@@ -11,6 +11,7 @@
 - form: 新增`suffix`属性,用于配置后缀内容
 - form: 新增远程下拉`ApiSelect`及示例
 - form: 新增`autoFocusFirstItem`配置。用于配置是否聚焦表单第一个输入框
+- useForm: 支持动态改变参数。可以传入`Ref`类型与`Computed`类型进行动态更改
 
 ### ⚡ Performance Improvements
 
@@ -18,7 +19,7 @@
 
 ### 🎫 Chores
 
-- 升级`ant-design-vue`到`2.0.0-rc.6`
+- 升级`ant-design-vue`到`2.0.0-rc.7`
 
 ### 🐛 Bug Fixes
 

+ 1 - 1
package.json

@@ -23,7 +23,7 @@
   "dependencies": {
     "@iconify/iconify": "^2.0.0-rc.4",
     "@vueuse/core": "^4.0.1",
-    "ant-design-vue": "^2.0.0-rc.6",
+    "ant-design-vue": "^2.0.0-rc.7",
     "apexcharts": "^3.23.0",
     "axios": "^0.21.1",
     "crypto-es": "^1.2.6",

+ 13 - 3
src/components/Form/src/BasicForm.vue

@@ -31,14 +31,24 @@
   import type { AdvanceState } from './types/hooks';
   import type { CSSProperties, Ref, WatchStopHandle } from 'vue';
 
-  import { defineComponent, reactive, ref, computed, unref, onMounted, watch, toRefs } from 'vue';
+  import {
+    defineComponent,
+    reactive,
+    ref,
+    computed,
+    unref,
+    onMounted,
+    watch,
+    toRefs,
+    toRaw,
+  } from 'vue';
   import { Form, Row } from 'ant-design-vue';
   import FormItem from './components/FormItem';
   import FormAction from './components/FormAction.vue';
 
   import { dateItemType } from './helper';
   import moment from 'moment';
-  import { cloneDeep } from 'lodash-es';
+  // import { cloneDeep } from 'lodash-es';
   import { deepMerge } from '/@/utils';
 
   import { useFormValues } from './hooks/useFormValues';
@@ -76,7 +86,7 @@
       // Get the basic configuration of the form
       const getProps = computed(
         (): FormProps => {
-          return deepMerge(cloneDeep(props), unref(propsRef));
+          return { ...props, ...unref(propsRef) } as FormProps;
         }
       );
 

+ 1 - 1
src/components/Form/src/components/ApiSelect.vue

@@ -38,7 +38,7 @@
         type: String as PropType<string>,
       },
       api: {
-        type: Function as PropType<(arg: Recordable) => Promise<OptionsItem[]>>,
+        type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>,
         default: null,
       },
       params: {

+ 12 - 3
src/components/Form/src/hooks/useForm.ts

@@ -1,15 +1,19 @@
-import { ref, onUnmounted, unref, nextTick } from 'vue';
+import { ref, onUnmounted, unref, nextTick, watchEffect } from 'vue';
 
 import { isInSetup } from '/@/utils/helper/vueHelper';
 import { isProdMode } from '/@/utils/env';
 import { error } from '/@/utils/log';
+import { getDynamicProps } from '/@/utils';
 
 import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form';
 import type { NamePath } from 'ant-design-vue/lib/form/interface';
+import type { DynamicProps } from '/@/types/utils';
 
 export declare type ValidateFields = (nameList?: NamePath[]) => Promise<Recordable>;
 
-export function useForm(props?: Partial<FormProps>): UseFormReturnType {
+type Props = Partial<DynamicProps<FormProps>>;
+
+export function useForm(props?: Props): UseFormReturnType {
   isInSetup();
 
   const formRef = ref<Nullable<FormActionType>>(null);
@@ -25,6 +29,7 @@ export function useForm(props?: Partial<FormProps>): UseFormReturnType {
     await nextTick();
     return form as FormActionType;
   }
+
   function register(instance: FormActionType) {
     isProdMode() &&
       onUnmounted(() => {
@@ -34,8 +39,12 @@ export function useForm(props?: Partial<FormProps>): UseFormReturnType {
     if (unref(loadedRef) && isProdMode() && instance === unref(formRef)) return;
 
     formRef.value = instance;
-    props && instance.setProps(props);
+
     loadedRef.value = true;
+
+    watchEffect(() => {
+      props && instance.setProps(getDynamicProps(props));
+    });
   }
 
   const methods: FormActionType = {

+ 2 - 4
src/components/Form/src/hooks/useFormEvents.ts

@@ -178,12 +178,10 @@ export function useFormEvents({
   }
 
   async function validateFields(nameList?: NamePath[] | undefined) {
-    const res = await unref(formElRef)?.validateFields(nameList || []);
-    return res;
+    return unref(formElRef)?.validateFields(nameList);
   }
-
   async function validate(nameList?: NamePath[] | undefined) {
-    return await unref(formElRef)?.validate(nameList || []);
+    return await unref(formElRef)?.validate(nameList);
   }
 
   async function clearValidate(name?: string | string[]) {

+ 4 - 0
src/components/Form/src/hooks/useLabelWidth.ts

@@ -18,6 +18,9 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<
 
     // If labelWidth is set globally, all items setting
     if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) {
+      labelCol.style = {
+        textAlign: 'left',
+      };
       return { labelCol, wrapperCol };
     }
     let width = labelWidth || globalLabelWidth;
@@ -27,6 +30,7 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<
     if (width) {
       width = isNumber(width) ? `${width}px` : width;
     }
+
     return {
       labelCol: { style: { width }, ...col },
       wrapperCol: { style: { width: `calc(100% - ${width})` }, ...wrapCol },

+ 10 - 3
src/components/Table/src/BasicTable.vue

@@ -68,8 +68,9 @@
   import { useEventListener } from '/@/hooks/event/useEventListener';
   import { basicProps } from './props';
   import { ROW_KEY } from './const';
-  import './style/index.less';
   import { useExpose } from '/@/hooks/core/useExpose';
+
+  import './style/index.less';
   export default defineComponent({
     props: basicProps,
     components: { Table, BasicForm },
@@ -87,6 +88,12 @@
         } as BasicTableProps;
       });
 
+      //  const getProps = computed(
+      //   (): FormProps => {
+      //     return deepMerge(toRaw(props), unref(innerPropsRef));
+      //   }
+      // );
+
       const { loadingRef } = useLoading(getMergeProps);
       const { getPaginationRef, setPagination } = usePagination(getMergeProps);
       const { getColumnsRef, setColumns } = useColumns(getMergeProps, getPaginationRef);
@@ -299,8 +306,8 @@
           loadingRef.value = loading;
         },
         setProps,
-        getSize: (): SizeType => {
-          return unref(getBindValues).size;
+        getSize: () => {
+          return unref(getBindValues).size as SizeType;
         },
       };
 

+ 0 - 1
src/components/Table/src/components/EditTableHeaderIcon.vue

@@ -16,6 +16,5 @@
         default: '',
       },
     },
-    setup() {},
   });
 </script>

+ 1 - 1
src/components/Table/src/components/TableSetting.vue

@@ -90,7 +90,6 @@
     SettingOutlined,
   } from '@ant-design/icons-vue';
   import { useFullscreen } from '/@/hooks/web/useFullScreen';
-
   import type { SizeType, TableSetting } from '../types/table';
   import { useI18n } from '/@/hooks/web/useI18n';
 
@@ -150,6 +149,7 @@
           init();
         }
       });
+
       function init() {
         let ret: Options[] = [];
         table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {

+ 2 - 2
src/components/Table/src/components/TableTitle.vue

@@ -13,10 +13,10 @@
     components: { BasicTitle },
     props: {
       title: {
-        type: [Function, String] as PropType<string | ((data: any) => string)>,
+        type: [Function, String] as PropType<string | ((data: Recordable) => string)>,
       },
       getSelectRows: {
-        type: Function as PropType<() => any[]>,
+        type: Function as PropType<() => Recordable[]>,
       },
       helpMessage: {
         type: [String, Array] as PropType<string | string[]>,

+ 1 - 1
src/components/Table/src/components/renderExpandIcon.tsx

@@ -1,7 +1,7 @@
 import { BasicArrow } from '/@/components/Basic';
 
 export default () => {
-  return (props: any) => {
+  return (props: Recordable) => {
     return (
       <BasicArrow
         onClick={(e: Event) => {

+ 6 - 6
src/components/Table/src/hooks/useTable.ts

@@ -1,24 +1,24 @@
 import type { BasicTableProps, TableActionType, FetchParams, BasicColumn } from '../types/table';
 import type { PaginationProps } from '../types/pagination';
 
-import { ref, getCurrentInstance, onUnmounted, unref } from 'vue';
+import { ref, onUnmounted, unref } from 'vue';
 import { isProdMode } from '/@/utils/env';
+import { isInSetup } from '/@/utils/helper/vueHelper';
 
 export function useTable(
   tableProps?: Partial<BasicTableProps>
 ): [(instance: TableActionType) => void, TableActionType] {
-  if (!getCurrentInstance()) {
-    throw new Error('Please put useTable function in the setup function!');
-  }
+  isInSetup();
 
-  const tableRef = ref<TableActionType | null>(null);
-  const loadedRef = ref<boolean | null>(false);
+  const tableRef = ref<Nullable<TableActionType>>(null);
+  const loadedRef = ref<Nullable<boolean>>(false);
 
   function register(instance: TableActionType) {
     onUnmounted(() => {
       tableRef.value = null;
       loadedRef.value = null;
     });
+
     if (unref(loadedRef) && isProdMode() && instance === unref(tableRef)) {
       return;
     }

+ 1 - 1
src/layouts/default/header/components/lock/LockAction.tsx

@@ -57,7 +57,7 @@ export default defineComponent({
               <p class={`${prefixCls}__header-name`}>{userStore.getUserInfoState.realName}</p>
             </div>
 
-            <BasicForm onRegister={registerForm} layout="vertical" />
+            <BasicForm onRegister={registerForm} />
 
             <div class={`${prefixCls}__footer`}>
               <Button type="primary" block class="mt-2" onClick={lock}>

+ 1 - 1
src/layouts/default/header/components/lock/LockModal.vue

@@ -12,7 +12,7 @@
         <p :class="`${prefixCls}__header-name`">{{ getRealName }}</p>
       </div>
 
-      <BasicForm @register="registerForm" layout="vertical" />
+      <BasicForm @register="registerForm" />
 
       <div :class="`${prefixCls}__footer`">
         <a-button type="primary" block class="mt-2" @click="handleLock">

+ 5 - 0
src/types/utils.ts

@@ -0,0 +1,5 @@
+import type { ComputedRef, Ref } from 'vue';
+
+export type DynamicProps<T> = {
+  [P in keyof T]: Ref<T[P]> | T[P] | ComputedRef<T[P]>;
+};

+ 12 - 0
src/utils/index.ts

@@ -1,4 +1,5 @@
 export const timestamp = () => +Date.now();
+import { unref } from 'vue';
 import { isObject } from '/@/utils/is';
 export const clamp = (n: number, min: number, max: number) => Math.min(max, Math.max(min, n));
 export const noop = () => {};
@@ -76,3 +77,14 @@ export function openWindow(
 
   window.open(url, target, feature.join(','));
 }
+
+// dynamic use hook props
+export function getDynamicProps<T, U>(props: T): Partial<U> {
+  const ret: Recordable = {};
+
+  Object.keys(props).map((key) => {
+    ret[key] = unref((props as Recordable)[key]);
+  });
+
+  return ret as Partial<U>;
+}

+ 2 - 1
src/views/demo/form/UseForm.vue

@@ -162,6 +162,7 @@
     components: { BasicForm, CollapseContainer },
     setup() {
       const { createMessage } = useMessage();
+
       const [register, { setProps }] = useForm({
         labelWidth: 120,
         schemas,
@@ -172,7 +173,7 @@
       return {
         register,
         schemas,
-        handleSubmit: (values: any) => {
+        handleSubmit: (values: Recordable) => {
           createMessage.success('click search,values:' + JSON.stringify(values));
         },
         setProps,

+ 2 - 2
src/views/demo/page/form/high/index.vue

@@ -6,10 +6,10 @@
 
     <div class="m-5">
       <a-card title="仓库管理" :bordered="false">
-        <BasicForm @register="register" layout="vertical" />
+        <BasicForm @register="register" />
       </a-card>
       <a-card title="任务管理" :bordered="false" class="mt-5">
-        <BasicForm @register="registerTask" layout="vertical" />
+        <BasicForm @register="registerTask" />
       </a-card>
       <a-card title="成员管理" :bordered="false" class="mt-5">
         <PersonTable ref="tableRef" />

+ 4 - 4
yarn.lock

@@ -1956,10 +1956,10 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
   dependencies:
     color-convert "^2.0.1"
 
-ant-design-vue@^2.0.0-rc.6:
-  version "2.0.0-rc.6"
-  resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.0.0-rc.6.tgz#f25f61cde1c75c32a78b536751731c0b223b6590"
-  integrity sha512-NRxzIC4CSM56MXYHdg3K2oTc+pkcSJd6BJtIBCxUsbFfbBGp+F7ei7C1bQDdHHos3o/Oe2iqGwzfrZ7+Ot2Uew==
+ant-design-vue@^2.0.0-rc.7:
+  version "2.0.0-rc.7"
+  resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.0.0-rc.7.tgz#5d83a7f13275574ec1fc1ea8c1fe8d9aa6de067c"
+  integrity sha512-QMStvwaLfV1Q3RaQ8D926aCkW6iqWBHXlNv7dBdTPvU8eeFXPaPKenLu1OTpSi+wpCncJqgumFOEcENPvh0nKw==
   dependencies:
     "@ant-design-vue/use" "^0.0.1-0"
     "@ant-design/icons-vue" "^5.1.7"