mirror of
				https://github.com/Tencent/tmagic-editor.git
				synced 2025-11-01 00:57:01 +08:00 
			
		
		
		
	feat(form): 支持表单差异对比
1、支持表单差异对比 2、支持在tab统计差异数量
This commit is contained in:
		
							parent
							
								
									c41af9d01d
								
							
						
					
					
						commit
						6610f30afd
					
				| @ -158,6 +158,9 @@ watch( | ||||
|     services?.codeBlockService.refreshAllRelations(); | ||||
|     refreshCodeList(); | ||||
|   }, | ||||
|   { | ||||
|     immediate: true, | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| watch( | ||||
|  | ||||
| @ -53,8 +53,7 @@ | ||||
|       padding: 5px; | ||||
|     } | ||||
| 
 | ||||
|     .is-text, | ||||
|     i { | ||||
|     .is-text > i { | ||||
|       color: $--font-color; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -15,6 +15,8 @@ | ||||
|         :key="item[keyProp] ?? index" | ||||
|         :config="item" | ||||
|         :model="values" | ||||
|         :last-values="lastValuesProcessed" | ||||
|         :is-compare="isCompare" | ||||
|         :label-width="item.labelWidth || labelWidth" | ||||
|         :step-active="stepActive" | ||||
|         :size="size" | ||||
| @ -26,8 +28,8 @@ | ||||
| 
 | ||||
| <script setup lang="ts" name="MForm"> | ||||
| import { provide, reactive, ref, toRaw, watch, watchEffect } from 'vue'; | ||||
| import { isEqual } from 'lodash-es'; | ||||
| import cloneDeep from 'lodash-es/cloneDeep'; | ||||
| import isEqual from 'lodash-es/isEqual'; | ||||
| 
 | ||||
| import { TMagicForm } from '@tmagic/design'; | ||||
| 
 | ||||
| @ -38,8 +40,14 @@ import type { FormConfig, FormState, FormValue, ValidateError } from './schema'; | ||||
| 
 | ||||
| const props = withDefaults( | ||||
|   defineProps<{ | ||||
|     /** 表单配置 */ | ||||
|     config: FormConfig; | ||||
|     /** 表单值 */ | ||||
|     initValues: Object; | ||||
|     /** 需对比的值(开启对比模式时传入) */ | ||||
|     lastValues?: Object; | ||||
|     /** 是否开启对比模式 */ | ||||
|     isCompare?: boolean; | ||||
|     parentValues?: Object; | ||||
|     labelWidth?: string; | ||||
|     disabled?: boolean; | ||||
| @ -54,6 +62,8 @@ const props = withDefaults( | ||||
|   { | ||||
|     config: () => [], | ||||
|     initValues: () => ({}), | ||||
|     lastValues: () => ({}), | ||||
|     isCompare: false, | ||||
|     parentValues: () => ({}), | ||||
|     labelWidth: '200px', | ||||
|     disabled: false, | ||||
| @ -70,6 +80,7 @@ const emit = defineEmits(['change', 'field-input', 'field-change']); | ||||
| const tMagicForm = ref<InstanceType<typeof TMagicForm>>(); | ||||
| const initialized = ref(false); | ||||
| const values = ref<FormValue>({}); | ||||
| const lastValuesProcessed = ref<FormValue>({}); | ||||
| const fields = new Map<string, any>(); | ||||
| 
 | ||||
| const requestFuc = getConfig('request') as Function; | ||||
| @ -79,8 +90,11 @@ const formState: FormState = reactive<FormState>({ | ||||
|   popperClass: props.popperClass, | ||||
|   config: props.config, | ||||
|   initValues: props.initValues, | ||||
|   isCompare: props.isCompare, | ||||
|   lastValues: props.lastValues, | ||||
|   parentValues: props.parentValues, | ||||
|   values, | ||||
|   lastValuesProcessed, | ||||
|   $emit: emit as (event: string, ...args: any[]) => void, | ||||
|   fields, | ||||
|   setField: (prop: string, field: any) => fields.set(prop, field), | ||||
| @ -98,6 +112,8 @@ const formState: FormState = reactive<FormState>({ | ||||
| 
 | ||||
| watchEffect(() => { | ||||
|   formState.initValues = props.initValues; | ||||
|   formState.lastValues = props.lastValues; | ||||
|   formState.isCompare = props.isCompare; | ||||
|   formState.config = props.config; | ||||
|   formState.keyProp = props.keyProp; | ||||
|   formState.popperClass = props.popperClass; | ||||
| @ -118,8 +134,20 @@ watch( | ||||
|       config: props.config, | ||||
|     }).then((value) => { | ||||
|       values.value = value; | ||||
|       // 非对比模式,初始化完成 | ||||
|       initialized.value = !props.isCompare; | ||||
|     }); | ||||
| 
 | ||||
|     if (props.isCompare) { | ||||
|       // 对比模式下初始化待对比的表单值 | ||||
|       initValue(formState, { | ||||
|         initValues: props.lastValues, | ||||
|         config: props.config, | ||||
|       }).then((value) => { | ||||
|         lastValuesProcessed.value = value; | ||||
|         initialized.value = true; | ||||
|       }); | ||||
|     } | ||||
|   }, | ||||
|   { immediate: true }, | ||||
| ); | ||||
| @ -130,6 +158,7 @@ const changeHandler = () => { | ||||
| 
 | ||||
| defineExpose({ | ||||
|   values, | ||||
|   lastValuesProcessed, | ||||
|   formState, | ||||
|   initialized, | ||||
| 
 | ||||
|  | ||||
| @ -2,6 +2,8 @@ | ||||
|   <TMagicCol v-show="display && config.type !== 'hidden'" :span="span"> | ||||
|     <Container | ||||
|       :model="model" | ||||
|       :lastValues="lastValues" | ||||
|       :is-compare="isCompare" | ||||
|       :config="config" | ||||
|       :prop="prop" | ||||
|       :label-width="config.labelWidth || labelWidth" | ||||
| @ -9,6 +11,7 @@ | ||||
|       :size="size" | ||||
|       :disabled="disabled" | ||||
|       @change="changeHandler" | ||||
|       @add-diff-count="onAddDiffCount" | ||||
|     ></Container> | ||||
|   </TMagicCol> | ||||
| </template> | ||||
| @ -25,6 +28,8 @@ import Container from './Container.vue'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|   model: any; | ||||
|   lastValues?: any; | ||||
|   isCompare?: boolean; | ||||
|   config: ChildConfig; | ||||
|   labelWidth?: string; | ||||
|   expandMore?: boolean; | ||||
| @ -34,9 +39,10 @@ const props = defineProps<{ | ||||
|   disabled?: boolean; | ||||
| }>(); | ||||
| 
 | ||||
| const emit = defineEmits(['change']); | ||||
| const emit = defineEmits(['change', 'addDiffCount']); | ||||
| 
 | ||||
| const mForm = inject<FormState | undefined>('mForm'); | ||||
| const display = computed(() => displayFunction(mForm, props.config.display, props)); | ||||
| const changeHandler = () => emit('change', props.model); | ||||
| const onAddDiffCount = () => emit('addDiffCount'); | ||||
| </script> | ||||
|  | ||||
| @ -20,6 +20,8 @@ | ||||
|       :size="size" | ||||
|       :is="tagName" | ||||
|       :model="model" | ||||
|       :last-values="lastValues" | ||||
|       :is-compare="isCompare" | ||||
|       :config="config" | ||||
|       :disabled="disabled" | ||||
|       :name="name" | ||||
| @ -28,15 +30,120 @@ | ||||
|       :expand-more="expand" | ||||
|       :label-width="itemLabelWidth" | ||||
|       @change="onChangeHandler" | ||||
|       @addDiffCount="onAddDiffCount" | ||||
|     ></component> | ||||
| 
 | ||||
|     <template v-else-if="type && display"> | ||||
|     <template v-else-if="type && display && !showDiff"> | ||||
|       <TMagicFormItem | ||||
|         :style="config.tip ? 'flex: 1' : ''" | ||||
|         :class="{ hidden: `${itemLabelWidth}` === '0' || !config.text }" | ||||
|         :prop="itemProp" | ||||
|         :label-width="itemLabelWidth" | ||||
|         :rules="rule" | ||||
|       > | ||||
|         <template #label><span v-html="type === 'checkbox' ? '' : config.text"></span></template> | ||||
|         <TMagicTooltip v-if="tooltip"> | ||||
|           <component | ||||
|             :key="key(config)" | ||||
|             :size="size" | ||||
|             :is="tagName" | ||||
|             :model="model" | ||||
|             :config="config" | ||||
|             :name="name" | ||||
|             :disabled="disabled" | ||||
|             :prop="itemProp" | ||||
|             @change="onChangeHandler" | ||||
|             @addDiffCount="onAddDiffCount" | ||||
|           ></component> | ||||
|           <template #content> | ||||
|             <div v-html="tooltip"></div> | ||||
|           </template> | ||||
|         </TMagicTooltip> | ||||
| 
 | ||||
|         <component | ||||
|           v-else | ||||
|           :key="key(config)" | ||||
|           :size="size" | ||||
|           :is="tagName" | ||||
|           :model="model" | ||||
|           :config="config" | ||||
|           :name="name" | ||||
|           :disabled="disabled" | ||||
|           :prop="itemProp" | ||||
|           @change="onChangeHandler" | ||||
|           @addDiffCount="onAddDiffCount" | ||||
|         ></component> | ||||
| 
 | ||||
|         <div v-if="extra" v-html="extra" class="m-form-tip"></div> | ||||
|       </TMagicFormItem> | ||||
| 
 | ||||
|       <TMagicTooltip v-if="config.tip" placement="left"> | ||||
|         <TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon> | ||||
|         <template #content> | ||||
|           <div v-html="config.tip"></div> | ||||
|         </template> | ||||
|       </TMagicTooltip> | ||||
|     </template> | ||||
| 
 | ||||
|     <!-- 对比 --> | ||||
|     <template v-else-if="type && display && showDiff"> | ||||
|       <!-- 上次内容 --> | ||||
|       <TMagicFormItem | ||||
|         :style="config.tip ? 'flex: 1' : ''" | ||||
|         :class="{ hidden: `${itemLabelWidth}` === '0' || !config.text }" | ||||
|         :prop="itemProp" | ||||
|         :label-width="itemLabelWidth" | ||||
|         :rules="rule" | ||||
|         style="background: #f7dadd" | ||||
|       > | ||||
|         <template #label><span v-html="type === 'checkbox' ? '' : config.text"></span></template> | ||||
|         <TMagicTooltip v-if="tooltip"> | ||||
|           <component | ||||
|             :key="key(config)" | ||||
|             :size="size" | ||||
|             :is="tagName" | ||||
|             :model="lastValues" | ||||
|             :config="config" | ||||
|             :name="name" | ||||
|             :disabled="disabled" | ||||
|             :prop="itemProp" | ||||
|             @change="onChangeHandler" | ||||
|           ></component> | ||||
|           <template #content> | ||||
|             <div v-html="tooltip"></div> | ||||
|           </template> | ||||
|         </TMagicTooltip> | ||||
| 
 | ||||
|         <component | ||||
|           v-else | ||||
|           :key="key(config)" | ||||
|           :size="size" | ||||
|           :is="tagName" | ||||
|           :model="lastValues" | ||||
|           :config="config" | ||||
|           :name="name" | ||||
|           :disabled="disabled" | ||||
|           :prop="itemProp" | ||||
|           @change="onChangeHandler" | ||||
|         ></component> | ||||
| 
 | ||||
|         <div v-if="extra" v-html="extra" class="m-form-tip"></div> | ||||
|       </TMagicFormItem> | ||||
| 
 | ||||
|       <TMagicTooltip v-if="config.tip" placement="left"> | ||||
|         <TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon> | ||||
|         <template #content> | ||||
|           <div v-html="config.tip"></div> | ||||
|         </template> | ||||
|       </TMagicTooltip> | ||||
|       <!-- 当前内容 --> | ||||
|       <TMagicFormItem | ||||
|         :style="config.tip ? 'flex: 1' : ''" | ||||
|         :class="{ hidden: `${itemLabelWidth}` === '0' || !config.text }" | ||||
|         :prop="itemProp" | ||||
|         :label-width="itemLabelWidth" | ||||
|         :rules="rule" | ||||
|         style="background: #def7da" | ||||
|       > | ||||
|         <template #label><span v-html="type === 'checkbox' ? '' : config.text"></span></template> | ||||
|         <TMagicTooltip v-if="tooltip"> | ||||
| @ -86,6 +193,8 @@ | ||||
|           v-for="item in items" | ||||
|           :key="key(item)" | ||||
|           :model="name || name === 0 ? model[name] : model" | ||||
|           :last-values="name || name === 0 ? lastValues[name] : lastValues" | ||||
|           :is-compare="isCompare" | ||||
|           :config="item" | ||||
|           :size="size" | ||||
|           :disabled="disabled" | ||||
| @ -94,6 +203,7 @@ | ||||
|           :label-width="itemLabelWidth" | ||||
|           :prop="itemProp" | ||||
|           @change="onChangeHandler" | ||||
|           @addDiffCount="onAddDiffCount" | ||||
|         ></Container> | ||||
|       </template> | ||||
|     </template> | ||||
| @ -107,8 +217,9 @@ | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts" name="MFormContainer"> | ||||
| import { computed, inject, ref, resolveComponent, watchEffect } from 'vue'; | ||||
| import { computed, inject, ref, resolveComponent, watch, watchEffect } from 'vue'; | ||||
| import { WarningFilled } from '@element-plus/icons-vue'; | ||||
| import { isEqual } from 'lodash-es'; | ||||
| 
 | ||||
| import { TMagicButton, TMagicFormItem, TMagicIcon, TMagicTooltip } from '@tmagic/design'; | ||||
| 
 | ||||
| @ -117,7 +228,10 @@ import { display as displayFunction, filterFunction, getRules } from '../utils/f | ||||
| 
 | ||||
| const props = withDefaults( | ||||
|   defineProps<{ | ||||
|     /** 表单值 */ | ||||
|     model: FormValue; | ||||
|     /** 需对比的值(开启对比模式时传入) */ | ||||
|     lastValues?: FormValue; | ||||
|     config: ChildConfig; | ||||
|     prop?: string; | ||||
|     disabled?: boolean; | ||||
| @ -125,15 +239,19 @@ const props = withDefaults( | ||||
|     expandMore?: boolean; | ||||
|     stepActive?: string | number; | ||||
|     size?: string; | ||||
|     /** 是否开启对比模式 */ | ||||
|     isCompare?: boolean; | ||||
|   }>(), | ||||
|   { | ||||
|     prop: '', | ||||
|     size: 'small', | ||||
|     expandMore: false, | ||||
|     lastValues: () => ({}), | ||||
|     isCompare: false, | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| const emit = defineEmits(['change']); | ||||
| const emit = defineEmits(['change', 'addDiffCount']); | ||||
| 
 | ||||
| const mForm = inject<FormState | undefined>('mForm'); | ||||
| 
 | ||||
| @ -141,6 +259,14 @@ const expand = ref(false); | ||||
| 
 | ||||
| const name = computed(() => props.config.name || ''); | ||||
| 
 | ||||
| // 是否展示两个版本的对比内容 | ||||
| const showDiff = computed(() => { | ||||
|   if (!props.isCompare) return false; | ||||
|   const curValue = name.value ? props.model[name.value] : props.model; | ||||
|   const lastValue = name.value ? props.lastValues[name.value] : props.lastValues; | ||||
|   return !isEqual(curValue, lastValue); | ||||
| }); | ||||
| 
 | ||||
| const items = computed(() => (props.config as ContainerCommonConfig).items); | ||||
| 
 | ||||
| const itemProp = computed(() => { | ||||
| @ -196,6 +322,21 @@ watchEffect(() => { | ||||
|   expand.value = props.expandMore; | ||||
| }); | ||||
| 
 | ||||
| // 监听是否展示对比内容,如果出现差异项则触发差异数计数事件 | ||||
| watch( | ||||
|   showDiff, | ||||
|   (showDiff) => { | ||||
|     if (type.value === 'hidden') return; | ||||
|     if (items.value && !props.config.text && type.value && display.value) return; | ||||
|     if (display.value && showDiff && type.value) { | ||||
|       emit('addDiffCount'); | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     immediate: true, | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| const expandHandler = () => (expand.value = !expand.value); | ||||
| 
 | ||||
| const key = (config: any) => config[mForm?.keyProps]; | ||||
| @ -236,6 +377,9 @@ const trimHandler = (trim: any, value: FormValue | number | string) => { | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| // 继续抛出给更高层级的组件 | ||||
| const onAddDiffCount = () => emit('addDiffCount'); | ||||
| 
 | ||||
| const onChangeHandler = async function (v: FormValue, key?: string) { | ||||
|   const { filter, onChange, trim, name, dynamicKey } = props.config as any; | ||||
|   let value: FormValue | number | string = v; | ||||
|  | ||||
| @ -24,6 +24,8 @@ | ||||
|           v-for="(item, index) in config.items" | ||||
|           :key="key(item, index)" | ||||
|           :model="name ? model[name] : model" | ||||
|           :lastValues="name ? lastValues[name] : lastValues" | ||||
|           :is-compare="isCompare" | ||||
|           :rules="name ? rules[name] : []" | ||||
|           :config="item" | ||||
|           :prop="prop" | ||||
| @ -31,6 +33,7 @@ | ||||
|           :labelWidth="lWidth" | ||||
|           :size="size" | ||||
|           @change="change" | ||||
|           @add-diff-count="onAddDiffCount()" | ||||
|         ></Container> | ||||
|       </div> | ||||
| 
 | ||||
| @ -42,6 +45,8 @@ | ||||
|         v-for="(item, index) in config.items" | ||||
|         :key="key(item, index)" | ||||
|         :model="name ? model[name] : model" | ||||
|         :lastValues="name ? lastValues[name] : lastValues" | ||||
|         :is-compare="isCompare" | ||||
|         :rules="name ? rules[name] : []" | ||||
|         :config="item" | ||||
|         :prop="prop" | ||||
| @ -49,6 +54,7 @@ | ||||
|         :size="size" | ||||
|         :disabled="disabled" | ||||
|         @change="change" | ||||
|         @addDiffCount="onAddDiffCount()" | ||||
|       ></Container> | ||||
|     </template> | ||||
|   </fieldset> | ||||
| @ -69,6 +75,8 @@ const props = withDefaults( | ||||
|     prop: string; | ||||
|     size?: string; | ||||
|     model: Record<string, any>; | ||||
|     lastValues?: Record<string, any>; | ||||
|     isCompare?: boolean; | ||||
|     config: FieldsetConfig; | ||||
|     rules?: any; | ||||
|     disabled?: boolean; | ||||
| @ -76,10 +84,12 @@ const props = withDefaults( | ||||
|   { | ||||
|     rules: {}, | ||||
|     prop: '', | ||||
|     lastValues: () => ({}), | ||||
|     isCompare: false, | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| const emit = defineEmits(['change']); | ||||
| const emit = defineEmits(['change', 'addDiffCount']); | ||||
| 
 | ||||
| const mForm = inject<FormState | undefined>('mForm'); | ||||
| 
 | ||||
| @ -113,4 +123,5 @@ if (props.config.checkbox && name.value) { | ||||
|     }, | ||||
|   ); | ||||
| } | ||||
| const onAddDiffCount = () => emit('addDiffCount'); | ||||
| </script> | ||||
|  | ||||
| @ -10,6 +10,8 @@ | ||||
|       v-for="(item, index) in model[name]" | ||||
|       :key="index" | ||||
|       :model="item" | ||||
|       :lastValues="getLastValues(lastValues[name], index)" | ||||
|       :is-compare="isCompare" | ||||
|       :config="config" | ||||
|       :prop="prop" | ||||
|       :index="index" | ||||
| @ -20,6 +22,7 @@ | ||||
|       @remove-item="removeHandler" | ||||
|       @swap-item="swapHandler" | ||||
|       @change="changeHandler" | ||||
|       @addDiffCount="onAddDiffCount()" | ||||
|     ></MFieldsGroupListItem> | ||||
| 
 | ||||
|     <TMagicButton @click="addHandler" size="small" :disabled="disabled" v-if="addable">添加组</TMagicButton> | ||||
| @ -41,6 +44,8 @@ import MFieldsGroupListItem from './GroupListItem.vue'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|   model: any; | ||||
|   lastValues?: any; | ||||
|   isCompare?: boolean; | ||||
|   config: GroupListConfig; | ||||
|   name: string; | ||||
|   labelWidth?: string; | ||||
| @ -49,7 +54,7 @@ const props = defineProps<{ | ||||
|   disabled?: boolean; | ||||
| }>(); | ||||
| 
 | ||||
| const emit = defineEmits(['change']); | ||||
| const emit = defineEmits(['change', 'addDiffCount']); | ||||
| 
 | ||||
| const mForm = inject<FormState | undefined>('mForm'); | ||||
| 
 | ||||
| @ -123,4 +128,7 @@ const toggleMode = () => { | ||||
|       text: null, | ||||
|     }))) as any; | ||||
| }; | ||||
| const onAddDiffCount = () => emit('addDiffCount'); | ||||
| 
 | ||||
| const getLastValues = (item: any, index: number) => item?.[index] || {}; | ||||
| </script> | ||||
|  | ||||
| @ -32,11 +32,14 @@ | ||||
|       v-if="expand" | ||||
|       :config="rowConfig" | ||||
|       :model="model" | ||||
|       :lastValues="lastValues" | ||||
|       :is-compare="isCompare" | ||||
|       :labelWidth="labelWidth" | ||||
|       :prop="`${prop}${prop ? '.' : ''}${String(index)}`" | ||||
|       :size="size" | ||||
|       :disabled="disabled" | ||||
|       @change="changeHandler" | ||||
|       @addDiffCount="onAddDiffCount()" | ||||
|     ></Container> | ||||
|   </div> | ||||
| </template> | ||||
| @ -54,6 +57,8 @@ import Container from './Container.vue'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|   model: any; | ||||
|   lastValues: any; | ||||
|   isCompare?: boolean; | ||||
|   groupModel: any[]; | ||||
|   config: GroupListConfig; | ||||
|   labelWidth?: string; | ||||
| @ -63,7 +68,7 @@ const props = defineProps<{ | ||||
|   disabled?: boolean; | ||||
| }>(); | ||||
| 
 | ||||
| const emit = defineEmits(['swap-item', 'remove-item', 'change']); | ||||
| const emit = defineEmits(['swap-item', 'remove-item', 'change', 'addDiffCount']); | ||||
| 
 | ||||
| const mForm = inject<FormState | undefined>('mForm'); | ||||
| const expand = ref(false); | ||||
| @ -122,4 +127,5 @@ const movable = () => { | ||||
|   } | ||||
|   return movable; | ||||
| }; | ||||
| const onAddDiffCount = () => emit('addDiffCount'); | ||||
| </script> | ||||
|  | ||||
| @ -23,11 +23,14 @@ | ||||
|             :key="item[mForm?.keyProp || '__key'] ?? index" | ||||
|             :config="item" | ||||
|             :model="name ? model[name] : model" | ||||
|             :lastValues="name ? lastValues[name] : lastValues" | ||||
|             :is-compare="isCompare" | ||||
|             :prop="prop" | ||||
|             :size="size" | ||||
|             :disabled="disabled" | ||||
|             :label-width="config.labelWidth || labelWidth" | ||||
|             @change="changeHandler" | ||||
|             @addDiffCount="onAddDiffCount()" | ||||
|           ></Container> | ||||
|         </div> | ||||
| 
 | ||||
| @ -40,11 +43,14 @@ | ||||
|           :key="item[mForm?.keyProp || '__key'] ?? index" | ||||
|           :config="item" | ||||
|           :model="name ? model[name] : model" | ||||
|           :lastValues="name ? lastValues[name] : lastValues" | ||||
|           :is-compare="isCompare" | ||||
|           :prop="prop" | ||||
|           :size="size" | ||||
|           :disabled="disabled" | ||||
|           :label-width="config.labelWidth || labelWidth" | ||||
|           @change="changeHandler" | ||||
|           @addDiffCount="onAddDiffCount()" | ||||
|         ></Container> | ||||
|       </template> | ||||
|     </div> | ||||
| @ -64,6 +70,8 @@ import Container from './Container.vue'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|   model: any; | ||||
|   lastValues?: any; | ||||
|   isCompare?: boolean; | ||||
|   config: PanelConfig; | ||||
|   name: string; | ||||
|   labelWidth?: string; | ||||
| @ -72,7 +80,7 @@ const props = defineProps<{ | ||||
|   disabled?: boolean; | ||||
| }>(); | ||||
| 
 | ||||
| const emit = defineEmits(['change']); | ||||
| const emit = defineEmits(['change', 'addDiffCount']); | ||||
| 
 | ||||
| const mForm = inject<FormState | undefined>('mForm'); | ||||
| 
 | ||||
| @ -83,4 +91,5 @@ const items = computed(() => props.config.items); | ||||
| const filter = (config: any) => filterFunction(mForm, config, props); | ||||
| 
 | ||||
| const changeHandler = () => emit('change', props.model); | ||||
| const onAddDiffCount = () => emit('addDiffCount'); | ||||
| </script> | ||||
|  | ||||
| @ -8,10 +8,13 @@ | ||||
|       :labelWidth="config.labelWidth || labelWidth" | ||||
|       :expandMore="expandMore" | ||||
|       :model="name ? model[name] : model" | ||||
|       :lastValues="name ? lastValues[name] : lastValues" | ||||
|       :is-compare="isCompare" | ||||
|       :prop="prop" | ||||
|       :size="size" | ||||
|       :disabled="disabled" | ||||
|       @change="changeHandler" | ||||
|       @add-diff-count="onAddDiffCount" | ||||
|     /> | ||||
|   </TMagicRow> | ||||
| </template> | ||||
| @ -27,6 +30,8 @@ import Col from './Col.vue'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|   model: any; | ||||
|   lastValues?: any; | ||||
|   isCompare?: boolean; | ||||
|   config: RowConfig; | ||||
|   name: string; | ||||
|   labelWidth?: string; | ||||
| @ -36,9 +41,10 @@ const props = defineProps<{ | ||||
|   disabled?: boolean; | ||||
| }>(); | ||||
| 
 | ||||
| const emit = defineEmits(['change']); | ||||
| const emit = defineEmits(['change', 'addDiffCount']); | ||||
| 
 | ||||
| const mForm = inject<FormState | undefined>('mForm'); | ||||
| 
 | ||||
| const changeHandler = () => emit('change', props.name ? props.model[props.name] : props.model); | ||||
| const onAddDiffCount = () => emit('addDiffCount'); | ||||
| </script> | ||||
|  | ||||
| @ -18,11 +18,14 @@ | ||||
|           :key="item[mForm?.keyProp || '__key']" | ||||
|           :config="item" | ||||
|           :model="step.name ? model[step.name] : model" | ||||
|           :lastValues="step.name ? lastValues[step.name] : lastValues" | ||||
|           :is-compare="isCompare" | ||||
|           :prop="`${step.name}`" | ||||
|           :size="size" | ||||
|           :disabled="disabled" | ||||
|           :label-width="config.labelWidth || labelWidth" | ||||
|           @change="changeHandler" | ||||
|           @addDiffCount="onAddDiffCount()" | ||||
|         ></Container> | ||||
|       </template> | ||||
|     </template> | ||||
| @ -41,6 +44,8 @@ import Container from './Container.vue'; | ||||
| const props = withDefaults( | ||||
|   defineProps<{ | ||||
|     model: any; | ||||
|     lastValues?: any; | ||||
|     isCompare?: boolean; | ||||
|     config: StepConfig; | ||||
|     stepActive?: number; | ||||
|     labelWidth?: string; | ||||
| @ -52,7 +57,7 @@ const props = withDefaults( | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| const emit = defineEmits(['change']); | ||||
| const emit = defineEmits(['change', 'addDiffCount']); | ||||
| 
 | ||||
| const mForm = inject<FormState | undefined>('mForm'); | ||||
| const active = ref(1); | ||||
| @ -69,4 +74,5 @@ const stepClick = (index: number) => { | ||||
| const changeHandler = () => { | ||||
|   emit('change', props.model); | ||||
| }; | ||||
| const onAddDiffCount = () => emit('addDiffCount'); | ||||
| </script> | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
|           style="width: 100%" | ||||
|           :row-key="config.rowKey || 'id'" | ||||
|           :data="data" | ||||
|           :lastData="lastData" | ||||
|           :border="config.border" | ||||
|           :max-height="config.maxHeight" | ||||
|           :default-expand-all="true" | ||||
| @ -108,8 +109,11 @@ | ||||
|                   :rules="column.rules" | ||||
|                   :config="makeConfig(column, scope.row)" | ||||
|                   :model="scope.row" | ||||
|                   :lastValues="lastData[scope.$index]" | ||||
|                   :is-compare="isCompare" | ||||
|                   :size="size" | ||||
|                   @change="$emit('change', model[modelName])" | ||||
|                   @addDiffCount="onAddDiffCount()" | ||||
|                 ></Container> | ||||
|               </template> | ||||
|             </TMagicTableColumn> | ||||
| @ -196,6 +200,8 @@ import Container from './Container.vue'; | ||||
| const props = withDefaults( | ||||
|   defineProps<{ | ||||
|     model: any; | ||||
|     lastValues?: any; | ||||
|     isCompare?: boolean; | ||||
|     config: TableConfig; | ||||
|     name: string; | ||||
|     prop?: string; | ||||
| @ -213,10 +219,12 @@ const props = withDefaults( | ||||
|     sortKey: '', | ||||
|     enableToggleMode: true, | ||||
|     showIndex: true, | ||||
|     lastValues: () => ({}), | ||||
|     isCompare: false, | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| const emit = defineEmits(['change', 'select']); | ||||
| const emit = defineEmits(['change', 'select', 'addDiffCount']); | ||||
| 
 | ||||
| let timer: any | null = null; | ||||
| const mForm = inject<FormState | undefined>('mForm'); | ||||
| @ -241,6 +249,15 @@ const data = computed(() => | ||||
|     : props.model[modelName.value], | ||||
| ); | ||||
| 
 | ||||
| const lastData = computed(() => | ||||
|   props.config.pagination | ||||
|     ? props.lastValues[modelName.value].filter( | ||||
|         (item: any, index: number) => | ||||
|           index >= pagecontext.value * pagesize.value && index + 1 <= (pagecontext.value + 1) * pagesize.value, | ||||
|       ) | ||||
|     : props.lastValues[modelName.value] || {}, | ||||
| ); | ||||
| 
 | ||||
| const sortChange = ({ prop, order }: SortProp) => { | ||||
|   if (order === 'ascending') { | ||||
|     props.model[modelName.value] = props.model[modelName.value].sort((a: any, b: any) => a[prop] - b[prop]); | ||||
| @ -593,6 +610,8 @@ const getProp = (index: number) => { | ||||
|   return `${prop.value}${prop.value ? '.' : ''}${index + 1 + pagecontext.value * pagesize.value - 1}`; | ||||
| }; | ||||
| 
 | ||||
| const onAddDiffCount = () => emit('addDiffCount'); | ||||
| 
 | ||||
| defineExpose({ | ||||
|   toggleRowSelection, | ||||
| }); | ||||
|  | ||||
| @ -18,25 +18,26 @@ | ||||
|         :label="filter(tab.title)" | ||||
|         :lazy="tab.lazy || false" | ||||
|       > | ||||
|         <template #label> | ||||
|           <span class="custom-tabs-label"> | ||||
|             {{ filter(tab.title) | ||||
|             }}<el-badge :hidden="!diffCount[tabIndex]" :value="diffCount[tabIndex]" class="diff-count-badge"></el-badge> | ||||
|           </span> | ||||
|         </template> | ||||
|         <Container | ||||
|           v-for="item in tabItems(tab)" | ||||
|           :key="item[mForm?.keyProp || '__key']" | ||||
|           :config="item" | ||||
|           :disabled="disabled" | ||||
|           :model=" | ||||
|             config.dynamic | ||||
|               ? (name ? model[name] : model)[tabIndex] | ||||
|               : tab.name | ||||
|               ? (name ? model[name] : model)[tab.name] | ||||
|               : name | ||||
|               ? model[name] | ||||
|               : model | ||||
|           " | ||||
|           :model="getValues(model, tabIndex, tab)" | ||||
|           :last-values="getValues(lastValues, tabIndex, tab)" | ||||
|           :is-compare="isCompare" | ||||
|           :prop="config.dynamic ? `${prop}${prop ? '.' : ''}${String(tabIndex)}` : prop" | ||||
|           :size="size" | ||||
|           :label-width="tab.labelWidth || labelWidth" | ||||
|           :expand-more="expandMore" | ||||
|           @change="changeHandler" | ||||
|           @addDiffCount="onAddDiffCount(tabIndex)" | ||||
|         ></Container> | ||||
|       </component> | ||||
|     </template> | ||||
| @ -54,6 +55,10 @@ import { display as displayFunc, filterFunction } from '../utils/form'; | ||||
| 
 | ||||
| import Container from './Container.vue'; | ||||
| 
 | ||||
| type DiffCount = { | ||||
|   [tabIndex: number]: number; | ||||
| }; | ||||
| 
 | ||||
| const uiComponent = getConfig('components').tabPane; | ||||
| 
 | ||||
| const getActive = (mForm: FormState | undefined, props: any, activeTabName: string) => { | ||||
| @ -83,8 +88,11 @@ const tabClick = (mForm: FormState | undefined, tab: any, props: any) => { | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
| const props = withDefaults( | ||||
|   defineProps<{ | ||||
|     model: any; | ||||
|     lastValues?: any; | ||||
|     isCompare?: boolean; | ||||
|     config: TabConfig; | ||||
|     name: string; | ||||
|     size?: string; | ||||
| @ -92,12 +100,18 @@ const props = defineProps<{ | ||||
|     prop?: string; | ||||
|     expandMore?: boolean; | ||||
|     disabled?: boolean; | ||||
| }>(); | ||||
|   }>(), | ||||
|   { | ||||
|     lastValues: () => ({}), | ||||
|     isCompare: false, | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| const emit = defineEmits(['change']); | ||||
| const emit = defineEmits(['change', 'addDiffCount']); | ||||
| 
 | ||||
| const mForm = inject<FormState | undefined>('mForm'); | ||||
| const activeTabName = ref(getActive(mForm, props, '')); | ||||
| const diffCount = ref<DiffCount>({}); | ||||
| 
 | ||||
| const tabs = computed(() => { | ||||
|   if (props.config.dynamic) { | ||||
| @ -170,4 +184,27 @@ const changeHandler = () => { | ||||
|     props.config.onChange(mForm, { model: props.model, prop: props.prop, config: props.config }); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const getValues = (model: any, tabIndex: number, tab: any) => { | ||||
|   const tabName = props.config.dynamic ? (model[props?.name] || model)[tabIndex] : tab.name; | ||||
|   let propName = props.name; | ||||
|   if (tabName) { | ||||
|     propName = (model[props?.name] || model)[tab.name]; | ||||
|   } | ||||
|   if (propName) { | ||||
|     return model[props.name]; | ||||
|   } | ||||
|   return model; | ||||
| }; | ||||
| 
 | ||||
| // 在tabs组件中收集事件触发次数,即该tab下的差异数 | ||||
| const onAddDiffCount = (tabIndex: number) => { | ||||
|   if (!diffCount.value[tabIndex]) { | ||||
|     diffCount.value[tabIndex] = 1; | ||||
|   } else { | ||||
|     diffCount.value[tabIndex] += 1; | ||||
|   } | ||||
|   // 继续抛出给更高层级的组件 | ||||
|   emit('addDiffCount'); | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| @ -28,6 +28,8 @@ export type FormState = { | ||||
|   config: FormConfig; | ||||
|   popperClass?: string; | ||||
|   initValues: FormValue; | ||||
|   lastValues: FormValue; | ||||
|   isCompare: boolean; | ||||
|   values: FormValue; | ||||
|   $emit: (event: string, ...args: any[]) => void; | ||||
|   keyProp?: string; | ||||
|  | ||||
| @ -7,3 +7,4 @@ | ||||
| @use "./panel.scss"; | ||||
| @use "./table.scss"; | ||||
| @use "./select.scss"; | ||||
| @use "./tabs.scss"; | ||||
|  | ||||
| @ -23,3 +23,7 @@ | ||||
| .magic-form-tab { | ||||
|   margin-bottom: 10px; | ||||
| } | ||||
| 
 | ||||
| .diff-count-badge { | ||||
|   top: -10px; | ||||
| } | ||||
|  | ||||
| @ -1,9 +1,21 @@ | ||||
| <template> | ||||
|   <div style="width: 100%"> | ||||
|   <div style="width: 100%; overflow-y: auto"> | ||||
|     <nav-menu :data="menu"></nav-menu> | ||||
|     <div class="diff-form"> | ||||
|       <div>开启表单对比功能</div> | ||||
|       <m-form | ||||
|         ref="form" | ||||
|         :config="diffFormConfig" | ||||
|         :is-compare="true" | ||||
|         :init-values="currentVersion" | ||||
|         :last-values="lastVersion" | ||||
|         size="small" | ||||
|         height="100%" | ||||
|       ></m-form> | ||||
|     </div> | ||||
|     <div class="title">表单字段展示</div> | ||||
|     <div class="form-content"> | ||||
|       <m-form ref="form" :config="config" :init-values="initValue" size="small" height="100%"></m-form> | ||||
| 
 | ||||
|       <magic-code-editor class="code-editor-content" :init-values="config" @save="change"></magic-code-editor> | ||||
|     </div> | ||||
|   </div> | ||||
| @ -30,6 +42,58 @@ const resultVisible = ref(false); | ||||
| const result = ref(''); | ||||
| const form = ref<InstanceType<typeof MForm>>(); | ||||
| 
 | ||||
| const diffFormConfig = ref([ | ||||
|   { | ||||
|     type: 'tab', | ||||
|     items: [ | ||||
|       { | ||||
|         title: 'tab1', | ||||
|         labelWidth: '80px', | ||||
|         items: [ | ||||
|           { | ||||
|             name: 'text1', | ||||
|             text: '文本字段1', | ||||
|           }, | ||||
|           { | ||||
|             name: 'text2', | ||||
|             text: '文本字段2', | ||||
|           }, | ||||
|           { | ||||
|             type: 'number', | ||||
|             text: '计数器', | ||||
|             name: 'number', | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|       { | ||||
|         title: 'tab2', | ||||
|         labelWidth: '80px', | ||||
|         items: [ | ||||
|           { | ||||
|             type: 'colorPicker', | ||||
|             text: '取色器', | ||||
|             name: 'colorPicker', | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
| ]); | ||||
| 
 | ||||
| const currentVersion = ref({ | ||||
|   text1: '当前版本的文本内容', | ||||
|   text2: '你好', | ||||
|   number: 10, | ||||
|   colorPicker: '#ffffff', | ||||
| }); | ||||
| 
 | ||||
| const lastVersion = ref({ | ||||
|   text1: '上一版本的文本内容', | ||||
|   text2: '你好', | ||||
|   number: 12, | ||||
|   colorPicker: '#000000', | ||||
| }); | ||||
| 
 | ||||
| const config = ref([ | ||||
|   { | ||||
|     text: '文本', | ||||
| @ -358,9 +422,16 @@ function change(value: string) { | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss"> | ||||
| .diff-form { | ||||
|   width: 500px; | ||||
|   margin: 20px 0 0 50px; | ||||
| } | ||||
| .title { | ||||
|   margin: 20px 0 0 50px; | ||||
| } | ||||
| .form-content { | ||||
|   display: flex; | ||||
|   height: calc(100% - 75px); | ||||
|   height: 800px; | ||||
| 
 | ||||
|   .code-editor-content, | ||||
|   .m-form { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user