FormContext.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import { type InjectionKey, inject, provide, type Ref, computed, ref, watch, type ComputedRef } from "vue";
  2. import type { FieldProps } from "./Field.vue";
  3. import type { RuleItem } from "async-validator";
  4. import { useCellContext } from "../basic/CellContext";
  5. /**
  6. * 校验触发时机
  7. * - blur: 失去焦点时触发校验
  8. * - change: 值改变时触发校验
  9. * - submit: 提交表单时触发校验
  10. */
  11. export type ValidTrigger = "blur" | "change" | "submit";
  12. /**
  13. * 表单项上下文
  14. */
  15. export type FormItemContext = {
  16. errorState: ComputedRef<boolean>,
  17. getFieldName: () => string,
  18. /**
  19. * 触发表单条目获得焦点事件
  20. */
  21. onFieldFocus: () => void;
  22. /**
  23. * 触发表单条目失去焦点事件
  24. */
  25. onFieldBlur: () => void;
  26. /**
  27. * 触发表单条目值改变事件
  28. * @param newValue 新值
  29. */
  30. onFieldChange: (newValue: unknown) => void;
  31. /**
  32. * 清除表单条目校验状态
  33. */
  34. clearValidate: () => void;
  35. /**
  36. * 设置表单条目点击事件监听器,设置后表单项允许点击,点击后会触发点击事件。
  37. * @param listener 点击事件监听器
  38. */
  39. setOnClickListener: (listener: (() => void)|undefined) => void;
  40. /**
  41. * 获取表单组件中的当前值
  42. * @returns 表单组件中的当前值
  43. */
  44. getFormModelValue(): any;
  45. /**
  46. * 表单项是否禁用的状态
  47. */
  48. disabled: Ref<boolean|undefined>,
  49. /**
  50. * 表单项是否只读的状态
  51. */
  52. readonly: Ref<boolean|undefined>,
  53. };
  54. export type FormItemInternalContext = {
  55. /**
  56. * 获取表单组件的实例引用,如果没有子组件,则返回 Field 自身引用。
  57. *
  58. * 本函数专用于动态表单,直接使用的情况可以在模板中绑定ref获取实例引用。
  59. *
  60. * 只有 Field 组件设置了 requireChildRef 回调才能返回子组件实例引用,否则只会返回 Field 组件实例引用。
  61. * @returns
  62. */
  63. getExpectedRef: () => any,
  64. getItemRules: () => RuleItem[],
  65. getFieldName: () => string,
  66. getValidateTrigger: () => ValidTrigger;
  67. getUniqueId: () => string,
  68. setErrorState: (errorMessage: string|null) => void;
  69. setBlurState(): void;
  70. };
  71. export type FormContext = {
  72. //由表单项组件调用
  73. onFieldFocus: (item: FormItemInternalContext) => void;
  74. onFieldBlur: (item: FormItemInternalContext) => void;
  75. onFieldChange: (item: FormItemInternalContext, newValue: unknown) => void;
  76. clearValidate: (item: FormItemInternalContext) => void;
  77. addFormItemField: (item: FormItemInternalContext) => number;
  78. removeFormItemField: (item: FormItemInternalContext) => void;
  79. //form props
  80. validateTrigger: Ref<ValidTrigger|undefined>;
  81. addRequireMark: Ref<boolean|undefined>;
  82. colon: Ref<boolean|undefined>;
  83. labelWidth: Ref<string|number|undefined>;
  84. labelAlign: Ref<"left"|"center"|"right"|undefined>;
  85. labelPosition: Ref<'top'|'left'|undefined>;
  86. labelFlex: Ref<number|undefined>;
  87. inputFlex: Ref<number|undefined>;
  88. showLabel: Ref<boolean|undefined>;
  89. name: Ref<string|undefined>;
  90. fieldProps: Ref<FieldProps|undefined>,
  91. disabled: Ref<boolean|undefined>,
  92. readonly: Ref<boolean|undefined>,
  93. getItemValue: (item: FormItemInternalContext) => unknown;
  94. getItemRequieed: (item: FormItemInternalContext) => boolean;
  95. };
  96. /**
  97. * 用于Props默认值回调中获取表单上下文
  98. * @returns FormContext
  99. */
  100. export function propGetFormContext() {
  101. return inject<FormContext>('formContext', null as any);
  102. }
  103. export const FormItemContextContextKey: InjectionKey<FormItemContext> = Symbol('FormItemContext');
  104. /**
  105. * 用于注入表单项上下文
  106. * @returns FormItemContext
  107. */
  108. export function useInjectFormItemContext() : FormItemContext {
  109. const context = inject<FormItemContext>(FormItemContextContextKey, null as any);
  110. provide(FormItemContextContextKey, null as any as FormItemContext);
  111. return context as FormItemContext;
  112. }
  113. /**
  114. * 用于注入表单上下文
  115. * @returns FormContext
  116. */
  117. export function useInjectFormContext() : FormContext {
  118. return inject<FormContext>('formContext', null as any);
  119. }
  120. /**
  121. * 用于注入表单项子组件值,用于实现表单项值的双向绑定。
  122. *
  123. * 组件可以通过返回的 `value` 属性获取当前值,通过 `updateValue` 方法更新值,
  124. * 即使外部未绑定 `modelValue` 属性,也可以正常工作。
  125. *
  126. * ```ts
  127. * const {
  128. value,
  129. updateValue,
  130. } = useFieldChildValueInjector(
  131. toRef(props, 'modelValue'),
  132. (v) => emit('update:modelValue', v)
  133. );
  134. * ```
  135. * @param propsModelValue 组件外部传入的modelValue
  136. * @param emit 组件外部的emit
  137. * @param secondParentContext 二级父组件上下文,用于更新二级父组件的值。
  138. * @param fieldClick 表单项点击事件监听器,设置后表单项允许点击,点击后会触发点击事件。
  139. * @param initialValue 初始值
  140. * @returns
  141. */
  142. export function useFieldChildValueInjector<T>(
  143. propsModelValue: Ref<T>,
  144. emit: (v: T) => void,
  145. secondParentContext?: {
  146. getValue: () => T,
  147. updateValue: (v: T) => void,
  148. },
  149. fieldClick?: () => void,
  150. initialValue?: T,
  151. ) {
  152. const cellContext = useCellContext();
  153. const context = useInjectFormItemContext();
  154. const formContext = useInjectFormContext();
  155. const shadowRefValue = ref(propsModelValue.value ?? context.getFormModelValue() ?? initialValue) as Ref<T>;
  156. const value = computed(() => {
  157. if (secondParentContext)
  158. shadowRefValue.value = secondParentContext.getValue();
  159. return shadowRefValue.value
  160. });
  161. watch(() => propsModelValue.value, (v) => {
  162. shadowRefValue.value = v;
  163. })
  164. /**
  165. * 更新表单项值
  166. * @param newValue 新值
  167. */
  168. function updateValue(newValue: T) {
  169. if (secondParentContext)
  170. secondParentContext.updateValue(newValue);
  171. else
  172. emit(newValue);
  173. shadowRefValue.value = newValue;
  174. context?.onFieldChange(newValue);
  175. }
  176. if (fieldClick) {
  177. if (context)
  178. context.setOnClickListener(fieldClick);
  179. else if (cellContext)
  180. cellContext.setOnClickListener(fieldClick);
  181. }
  182. const disabled = computed(() => formContext.disabled.value || context.disabled.value);
  183. const readonly = computed(() => formContext.readonly.value || context.readonly.value);
  184. return {
  185. /**
  186. * 临时值
  187. */
  188. value: value,
  189. updateValue,
  190. /**
  191. * 表单项上下文
  192. */
  193. context,
  194. /**
  195. * 表单上下文
  196. */
  197. formContext,
  198. /**
  199. * 指示顶层由表单和表单项设置的禁用状态
  200. */
  201. disabled,
  202. /**
  203. * 指示顶层由表单和表单项设置的只读状态
  204. */
  205. readonly,
  206. }
  207. }