DynamicForm.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <template>
  2. <Form
  3. ref="formEditor"
  4. :name="name"
  5. v-bind="finalOptions.formAdditionaProps"
  6. :model="model || {}"
  7. :rules="finalOptions.formRules"
  8. :disabled="finalOptions.disabled"
  9. :readonly="finalOptions.readonly"
  10. @submit="(e) => emit('submit', e)"
  11. @submitFailed="() => emit('finishFailed')"
  12. >
  13. <DynamicFormRoot
  14. :options="finalOptions"
  15. :model="model"
  16. :name="name"
  17. />
  18. </Form>
  19. </template>
  20. <script setup lang="ts">
  21. import { computed, onMounted, provide, ref, toRef, toRefs, type PropType } from 'vue';
  22. import Form, { type FormInstance } from '../form/Form.vue';
  23. import {
  24. type IDynamicFormOptions, type IDynamicFormItem, type IDynamicFormRef,
  25. type IDynamicFormObject, defaultDynamicFormOptions,
  26. type IDynamicFormMessageCenter,
  27. type IDynamicFormMessageCenterCallback,
  28. MESSAGE_RELOAD
  29. } from '.';
  30. import DynamicFormRoot from './nest/DynamicFormRoot.vue';
  31. const props = defineProps({
  32. /**
  33. * 动态表单选项
  34. */
  35. options: {
  36. type: Object as PropType<IDynamicFormOptions>,
  37. default: null
  38. },
  39. /**
  40. * 表单数据模型
  41. */
  42. model: {
  43. type: Object,
  44. default: null
  45. },
  46. /**
  47. * 表单名称, 设置到表单组件上。
  48. */
  49. name: {
  50. type: String,
  51. default: ''
  52. },
  53. /**
  54. * 全局参数。用于向每个表单项的参数中添加额外的参数,可以在回调中的 formGlobalParams 中访问。
  55. */
  56. globalParams: {
  57. type: Object as PropType<IDynamicFormObject>,
  58. default: null
  59. },
  60. });
  61. const emit = defineEmits(['ready', 'submit', 'finish', 'finishFailed']);
  62. const { options, model, name } = toRefs(props);
  63. const finalOptions = computed<IDynamicFormOptions>(() => ({
  64. ...defaultDynamicFormOptions,
  65. ...options.value,
  66. }));
  67. provide('rawModel', model);
  68. provide('globalParams', toRef(props, 'globalParams'));
  69. provide('finalOptions', finalOptions);
  70. const formEditor = ref<FormInstance>();
  71. const widgetsRefMap = ref(new Map<string,() => unknown>());
  72. const messageCenterMap = new Map<string, IDynamicFormMessageCenterCallback>();
  73. provide('widgetsRefMap', widgetsRefMap);
  74. provide('messageCenter', {
  75. addInstance: (name: string, fn: IDynamicFormMessageCenterCallback) => messageCenterMap.set(name, fn),
  76. removeInstance: (name: string) => messageCenterMap.delete(name),
  77. } as IDynamicFormMessageCenter);
  78. //获取组件引用
  79. function getFormItemControlRef(key: string) {
  80. return widgetsRefMap.value.get(key)?.();
  81. }
  82. //通过路径访问
  83. function accessFormModel(keyName: string, isSet: boolean, setValue: unknown) : unknown {
  84. const keys = keyName.split('.');
  85. let ret : unknown = undefined;
  86. let obj = model.value as Record<string, unknown>;
  87. let keyIndex = 0;
  88. let key = keys[keyIndex];
  89. while (obj) {
  90. const leftIndex = key.indexOf('[');
  91. if (leftIndex > 0 && key.endsWith(']')) {
  92. const arr = obj[key.substring(0, leftIndex)] as Record<string, unknown>[];
  93. const index = parseInt(key.substring(leftIndex + 1, key.length - 1))
  94. obj = arr[index];
  95. if (keyIndex >= keys.length - 1) {
  96. ret = obj;
  97. if (isSet) arr[index] = setValue as Record<string, unknown>;
  98. }
  99. } else {
  100. const newObj = obj[key] as Record<string, unknown>;
  101. if (keyIndex >= keys.length - 1) {
  102. ret = newObj;
  103. if (isSet)
  104. obj[key] = setValue as Record<string, unknown>;
  105. }
  106. obj = newObj;
  107. }
  108. if (keyIndex < keys.length - 1)
  109. key = keys[++keyIndex];
  110. else
  111. break;
  112. }
  113. return ret;
  114. }
  115. //发送通知消息
  116. function dispatchMessage(messageName: string, data?: unknown, receiveFilter?: RegExp) {
  117. for (const iterator of messageCenterMap) {
  118. if (!receiveFilter || receiveFilter.test(iterator[0]))
  119. iterator[1](messageName, data);
  120. }
  121. }
  122. //发送重新加载消息
  123. function dispatchReload() {
  124. dispatchMessage(MESSAGE_RELOAD);
  125. }
  126. //初始化默认值到模型
  127. function initDefaultValuesToModel() {
  128. function loopItems(key: string, parentKey: string, type: string, items: IDynamicFormItem[]) {
  129. let i = 0;
  130. for (const item of items) {
  131. let currentKey = key;
  132. switch (type) {
  133. case 'flat-simple':
  134. case 'flat-group':
  135. currentKey = (parentKey ? parentKey + '.' : '') + item.name;
  136. break;
  137. default:
  138. case 'object':
  139. case 'object-group':
  140. currentKey = (key ? key + '.' : '') + item.name;
  141. break
  142. case 'array':
  143. currentKey = (parentKey ? parentKey + '.' : '') + `[${i}]`;
  144. break;
  145. case 'array-object':
  146. currentKey = (parentKey ? parentKey + '.' : '') + `[${i}]` + item.name;
  147. break;
  148. }
  149. if (item.children) {
  150. loopItems(currentKey, key, item.type || '', item.children);
  151. }
  152. //console.log(currentKey);
  153. if (item.defaultValue !== undefined) {
  154. const oldValue = accessFormModel(currentKey, false, undefined);
  155. if (oldValue !== undefined && oldValue !== null)
  156. continue;
  157. accessFormModel(currentKey, true, typeof item.defaultValue === 'function' ? item.defaultValue() : item.defaultValue);
  158. }
  159. i++;
  160. }
  161. }
  162. loopItems('', '', '', finalOptions.value.formItems);
  163. }
  164. //获取当前表单中可见的所有字段名
  165. function getVisibleFormNames() {
  166. return Array.from(messageCenterMap.keys());
  167. }
  168. onMounted(() => {
  169. setTimeout(() => {
  170. emit('ready');
  171. }, 400);
  172. });
  173. const formRef : IDynamicFormRef = {
  174. initDefaultValuesToModel,
  175. getVisibleFormNames,
  176. getFormRef() {
  177. if (!formEditor.value)
  178. throw new Error('Form instance is not create.');
  179. return formEditor.value
  180. },
  181. getFormItemControlRef: getFormItemControlRef as any,
  182. submit() { return this.getFormRef().validate(); },
  183. validate() { return this.getFormRef().validate(); },
  184. setValueByPath: (path: string|string[], value: unknown) => {
  185. if (Array.isArray(path))
  186. path = path.join('.');
  187. return accessFormModel(path, true, value);
  188. },
  189. getValueByPath: (path: string|string[]) => {
  190. if (Array.isArray(path))
  191. path = path.join('.');
  192. return accessFormModel(path, false, undefined);
  193. },
  194. dispatchMessage,
  195. dispatchReload,
  196. };
  197. provide('formRef', formRef);
  198. provide('formName', name.value || 'unnamed');
  199. defineExpose(formRef);
  200. </script>