| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- <template>
- <Form
- ref="formEditor"
- :name="name"
- v-bind="finalOptions.formAdditionaProps"
- :model="model || {}"
- :rules="finalOptions.formRules"
- :disabled="finalOptions.disabled"
- :readonly="finalOptions.readonly"
- @submit="(e) => emit('submit', e)"
- @submitFailed="() => emit('finishFailed')"
- >
- <!--空显示-->
- <slot name="empty" v-if="options.formItems?.length == 0 || !model">
- <div v-if="options.emptyText" class="dynamic-form-item-empty">{{ options.emptyText }}</div>
- </slot>
- <Alert
- v-else-if="(typeof model !== 'object' && !options.suppressRootError)"
- type="warning"
- message="DynamicForm: model is not a object!"
- :description="`At form ${name || 'unnamed'} Root`"
- />
- <template v-else>
- <template v-for="(item, index) in options.formItems" :key="item.name">
- <template v-if="item.type === 'insertion'">
- <slot name="insertion" :data="item" />
- </template>
- <!--表单条目渲染核心-->
- <DynamicFormItemContainer
- v-else
- :item="item"
- :name="item.name"
- :rawModel="finalModel"
- :model="finalModel[item.name]"
- :parentModel="finalModel"
- :isFirst="index === 0"
- :isLast="index === options.formItems.length - 1"
- @update:model="(v: unknown) => finalModel[item.name] = v"
- :disabled="options.disabled"
- >
- <template #arrayButtonAdd="props">
- <slot name="formArrayButtonAdd" :onClick="props.onClick" />
- </template>
- <template #arrayButtons="props">
- <slot name="formArrayButtons"
- :onDeleteClick="props.onDeleteClick"
- :onUpClick="props.onUpClick"
- :onDownClick="props.onDownClick"
- />
- </template>
- <template #formCeil="values">
- <slot name="formCeil" :data="values.data" />
- </template>
- </DynamicFormItemContainer>
- </template>
- <slot name="endButton" />
- </template>
- </Form>
- </template>
- <script setup lang="ts">
- import { computed, onMounted, provide, ref, toRef, toRefs, type PropType } from 'vue';
- import Form, { type FormInstance } from '../form/Form.vue';
- import {
- type IDynamicFormOptions, type IDynamicFormItem, type IDynamicFormRef,
- type IDynamicFormObject, defaultDynamicFormOptions,
- type IDynamicFormMessageCenter,
- type IDynamicFormMessageCenterCallback,
- MESSAGE_RELOAD,
- type IDynamicFormWidgetRef
- } from '.';
- import DynamicFormItemContainer from './nest/DynamicFormItemContainer.vue';
- import Alert from '@/components/feedback/Alert.vue';
- const props = defineProps({
- /**
- * 动态表单选项
- */
- options: {
- type: Object as PropType<IDynamicFormOptions>,
- default: null
- },
- /**
- * 表单数据模型
- */
- model: {
- type: Object,
- default: null
- },
- /**
- * 表单名称, 设置到表单组件上。
- */
- name: {
- type: String,
- default: ''
- },
- /**
- * 全局参数。用于向每个表单项的参数中添加额外的参数,可以在回调中的 formGlobalParams 中访问。
- */
- globalParams: {
- type: Object as PropType<IDynamicFormObject>,
- default: null
- },
- });
- const emit = defineEmits(['ready', 'submit', 'finish', 'finishFailed']);
- const { options, model, name } = toRefs(props);
- const finalOptions = computed<IDynamicFormOptions>(() => ({
- ...defaultDynamicFormOptions,
- ...options.value,
- }));
- const finalModel = computed(() => {
- if (typeof props.model !== 'object')
- return {};
- return props.model;
- });
- provide('rawModel', model);
- provide('globalParams', toRef(props, 'globalParams'));
- provide('finalOptions', finalOptions);
- const formEditor = ref<FormInstance>();
- const widgetsRefMap = new Map<string, IDynamicFormWidgetRef>();
- const widgetsRefTypesMap = new Map<string, IDynamicFormWidgetRef[]>();
- const messageCenterMap = new Map<string, IDynamicFormMessageCenterCallback>();
- provide('messageCenter', {
- addInstance: (name: string, fn: IDynamicFormMessageCenterCallback) => messageCenterMap.set(name, fn),
- removeInstance: (name: string) => messageCenterMap.delete(name),
- addWidgetRef: (name: string, type: string, ref: IDynamicFormWidgetRef) => {
- const refs = widgetsRefTypesMap.get(name) || [];
- refs.push(ref);
- widgetsRefTypesMap.set(type, refs);
- },
- removeWidgetRef: (name: string, type: string, ref: IDynamicFormWidgetRef) => {
- const refs = widgetsRefTypesMap.get(name) || [];
- widgetsRefTypesMap.set(type, refs.filter((r) => r !== ref));
- },
- } as IDynamicFormMessageCenter);
- //获取组件引用
- function getFormItemControlRef(key: string) {
- return widgetsRefMap.get(key)?.();
- }
- //获取组件引用组
- function getFormItemControlRefsByType(type: string) {
- return (widgetsRefTypesMap.get(type) || []).map((ref) => ref());
- }
- //通过路径访问
- function accessFormModel(keyName: string, isSet: boolean, setValue: unknown) : unknown {
- const keys = keyName.split('.');
- let ret : unknown = undefined;
- let obj = model.value as Record<string, unknown>;
- let keyIndex = 0;
- let key = keys[keyIndex];
- while (obj) {
- const leftIndex = key.indexOf('[');
- if (leftIndex > 0 && key.endsWith(']')) {
- const arr = obj[key.substring(0, leftIndex)] as Record<string, unknown>[];
- const index = parseInt(key.substring(leftIndex + 1, key.length - 1))
- obj = arr[index];
- if (keyIndex >= keys.length - 1) {
- ret = obj;
- if (isSet) arr[index] = setValue as Record<string, unknown>;
- }
- } else {
- const newObj = obj[key] as Record<string, unknown>;
- if (keyIndex >= keys.length - 1) {
- ret = newObj;
- if (isSet)
- obj[key] = setValue as Record<string, unknown>;
- }
- obj = newObj;
- }
- if (keyIndex < keys.length - 1)
- key = keys[++keyIndex];
- else
- break;
- }
- return ret;
- }
- //发送通知消息
- function dispatchMessage(messageName: string, data?: unknown, receiveFilter?: RegExp) {
- for (const iterator of messageCenterMap) {
- if (!receiveFilter || receiveFilter.test(iterator[0]))
- iterator[1](messageName, data);
- }
- }
- //发送重新加载消息
- function dispatchReload() {
- dispatchMessage(MESSAGE_RELOAD);
- }
- //初始化默认值到模型
- function initDefaultValuesToModel() {
- function loopItems(key: string, parentKey: string, type: string, items: IDynamicFormItem[]) {
- let i = 0;
- for (const item of items) {
- let currentKey = key;
- switch (type) {
- case 'flat-simple':
- case 'flat-group':
- currentKey = (parentKey ? parentKey + '.' : '') + item.name;
- break;
- default:
- case 'object':
- case 'object-group':
- currentKey = (key ? key + '.' : '') + item.name;
- break
- case 'array':
- currentKey = (parentKey ? parentKey + '.' : '') + `[${i}]`;
- break;
- case 'array-object':
- currentKey = (parentKey ? parentKey + '.' : '') + `[${i}]` + item.name;
- break;
- }
- if (item.children) {
- loopItems(currentKey, key, item.type || '', item.children);
- }
- //console.log(currentKey);
- if (item.defaultValue !== undefined) {
- const oldValue = accessFormModel(currentKey, false, undefined);
- if (oldValue !== undefined && oldValue !== null)
- continue;
- accessFormModel(currentKey, true, typeof item.defaultValue === 'function' ? item.defaultValue() : item.defaultValue);
- }
- i++;
- }
- }
- loopItems('', '', '', finalOptions.value.formItems);
- }
- //获取当前表单中可见的所有字段名
- function getVisibleFormNames() {
- return Array.from(messageCenterMap.keys());
- }
- onMounted(() => {
- setTimeout(() => {
- emit('ready');
- }, 400);
- });
- const formRef : IDynamicFormRef = {
- initDefaultValuesToModel,
- getVisibleFormNames,
- getFormRef() {
- if (!formEditor.value)
- throw new Error('Form instance is not create.');
- return formEditor.value
- },
- getFormItemControlRefsByType: getFormItemControlRefsByType as any,
- getFormItemControlRef: getFormItemControlRef as any,
- submit() { return this.getFormRef().validate(); },
- validate() { return this.getFormRef().validate(); },
- setValueByPath: (path: string|string[], value: unknown) => {
- if (Array.isArray(path))
- path = path.join('.');
- return accessFormModel(path, true, value);
- },
- getValueByPath: (path: string|string[]) => {
- if (Array.isArray(path))
- path = path.join('.');
- return accessFormModel(path, false, undefined);
- },
- dispatchMessage,
- dispatchReload,
- };
- provide('formRef', formRef);
- provide('formName', name.value || 'unnamed');
- defineExpose(formRef);
- </script>
|