useFormConfigHook.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /**
  2. * 通用表单配置hook
  3. * @param config 表单配置
  4. * @param data 表单数据
  5. * @param rules 表单规则
  6. * @returns initRuleFormData 初始化表单数据
  7. * @returns ruleFormData 表单数据
  8. * @returns formRules 表单规则
  9. * @returns cloneRuleFormData 克隆表单数据
  10. * @returns validateFormData 验证表单数据
  11. * @returns beforeRouteLeave 离开页面前的确认
  12. * @description 用于RuleForm的配置
  13. * @author Chauncey
  14. */
  15. import { ref, reactive, watch, onUnmounted } from 'vue';
  16. import type { FormRules } from 'element-plus';
  17. import { cloneDeep, isEqual } from 'lodash-es';
  18. import { onBeforeRouteLeave } from 'vue-router';
  19. import type { FormConfig } from '@/types/basic-form';
  20. import { msgConfirm } from '@/utils/element-plus/messageBox';
  21. export const useFormConfigHook = <T extends Record<string, any> = Record<string, any>>(
  22. config: FormConfig[],
  23. data: T,
  24. rules?: FormRules<T>,
  25. ) => {
  26. const ruleFormConfig = ref<FormConfig[]>(config);
  27. const ruleFormData = reactive<T>(cloneDeep(data));
  28. const initRuleFormData = ref<T>(cloneDeep(data));
  29. const formRules = reactive<FormRules<T>>(rules || {});
  30. const routeLeaveGuardRegistered = ref(false);
  31. let stopAutoSyncWatch: (() => void) | null = null;
  32. let autoSyncTimer: ReturnType<typeof setTimeout> | null = null;
  33. let removeInteractionListeners: (() => void) | null = null;
  34. const stopAutoSyncBaseline = () => {
  35. if (stopAutoSyncWatch) {
  36. stopAutoSyncWatch();
  37. stopAutoSyncWatch = null;
  38. }
  39. if (autoSyncTimer) {
  40. clearTimeout(autoSyncTimer);
  41. autoSyncTimer = null;
  42. }
  43. if (removeInteractionListeners) {
  44. removeInteractionListeners();
  45. removeInteractionListeners = null;
  46. }
  47. };
  48. const startAutoSyncBaseline = () => {
  49. stopAutoSyncBaseline();
  50. stopAutoSyncWatch = watch(
  51. () => ruleFormData,
  52. () => {
  53. initRuleFormData.value = cloneDeep(ruleFormData as T);
  54. },
  55. { deep: true, flush: 'post' },
  56. );
  57. const stopByUserInteraction = () => {
  58. stopAutoSyncBaseline();
  59. };
  60. if (typeof window !== 'undefined') {
  61. const events: Array<keyof WindowEventMap> = ['input', 'change', 'keydown', 'compositionend'];
  62. events.forEach((eventName) => {
  63. window.addEventListener(eventName, stopByUserInteraction, true);
  64. });
  65. removeInteractionListeners = () => {
  66. events.forEach((eventName) => {
  67. window.removeEventListener(eventName, stopByUserInteraction, true);
  68. });
  69. };
  70. }
  71. autoSyncTimer = setTimeout(() => {
  72. stopAutoSyncBaseline();
  73. }, 1500);
  74. };
  75. const cloneRuleFormData = () => {
  76. if (routeLeaveGuardRegistered.value) return;
  77. initRuleFormData.value = cloneDeep(ruleFormData as T);
  78. };
  79. const hasFormChanged = () => {
  80. return !isEqual(initRuleFormData.value, ruleFormData);
  81. };
  82. const beforeRouteLeave = () => {
  83. if (routeLeaveGuardRegistered.value) return;
  84. initRuleFormData.value = cloneDeep(ruleFormData as T);
  85. routeLeaveGuardRegistered.value = true;
  86. startAutoSyncBaseline();
  87. onBeforeRouteLeave((to, from, next) => {
  88. const hasChange = hasFormChanged();
  89. if (!hasChange) {
  90. next();
  91. return;
  92. }
  93. setTimeout(() => {
  94. msgConfirm('当前页面存在修改,是否确认离开当前页面?', '提示', {
  95. confirmButtonText: '确认',
  96. cancelButtonText: '取消',
  97. customClass: 'customMessageBox--warning',
  98. })
  99. .then(() => {
  100. next();
  101. })
  102. .catch(() => {
  103. next(false);
  104. });
  105. }, 200);
  106. });
  107. };
  108. onUnmounted(() => {
  109. stopAutoSyncBaseline();
  110. });
  111. return {
  112. ruleFormConfig,
  113. ruleFormData,
  114. formRules,
  115. cloneRuleFormData,
  116. beforeRouteLeave,
  117. };
  118. };