feat(form): 支持表单差异对比

1、支持表单差异对比
2、支持在tab统计差异数量
This commit is contained in:
parisma 2023-03-01 16:52:16 +08:00 committed by roymondchen
parent c41af9d01d
commit 6610f30afd
17 changed files with 399 additions and 38 deletions

View File

@ -158,6 +158,9 @@ watch(
services?.codeBlockService.refreshAllRelations(); services?.codeBlockService.refreshAllRelations();
refreshCodeList(); refreshCodeList();
}, },
{
immediate: true,
},
); );
watch( watch(

View File

@ -53,8 +53,7 @@
padding: 5px; padding: 5px;
} }
.is-text, .is-text > i {
i {
color: $--font-color; color: $--font-color;
} }

View File

@ -15,6 +15,8 @@
:key="item[keyProp] ?? index" :key="item[keyProp] ?? index"
:config="item" :config="item"
:model="values" :model="values"
:last-values="lastValuesProcessed"
:is-compare="isCompare"
:label-width="item.labelWidth || labelWidth" :label-width="item.labelWidth || labelWidth"
:step-active="stepActive" :step-active="stepActive"
:size="size" :size="size"
@ -26,8 +28,8 @@
<script setup lang="ts" name="MForm"> <script setup lang="ts" name="MForm">
import { provide, reactive, ref, toRaw, watch, watchEffect } from 'vue'; import { provide, reactive, ref, toRaw, watch, watchEffect } from 'vue';
import { isEqual } from 'lodash-es';
import cloneDeep from 'lodash-es/cloneDeep'; import cloneDeep from 'lodash-es/cloneDeep';
import isEqual from 'lodash-es/isEqual';
import { TMagicForm } from '@tmagic/design'; import { TMagicForm } from '@tmagic/design';
@ -38,8 +40,14 @@ import type { FormConfig, FormState, FormValue, ValidateError } from './schema';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
/** 表单配置 */
config: FormConfig; config: FormConfig;
/** 表单值 */
initValues: Object; initValues: Object;
/** 需对比的值(开启对比模式时传入) */
lastValues?: Object;
/** 是否开启对比模式 */
isCompare?: boolean;
parentValues?: Object; parentValues?: Object;
labelWidth?: string; labelWidth?: string;
disabled?: boolean; disabled?: boolean;
@ -54,6 +62,8 @@ const props = withDefaults(
{ {
config: () => [], config: () => [],
initValues: () => ({}), initValues: () => ({}),
lastValues: () => ({}),
isCompare: false,
parentValues: () => ({}), parentValues: () => ({}),
labelWidth: '200px', labelWidth: '200px',
disabled: false, disabled: false,
@ -70,6 +80,7 @@ const emit = defineEmits(['change', 'field-input', 'field-change']);
const tMagicForm = ref<InstanceType<typeof TMagicForm>>(); const tMagicForm = ref<InstanceType<typeof TMagicForm>>();
const initialized = ref(false); const initialized = ref(false);
const values = ref<FormValue>({}); const values = ref<FormValue>({});
const lastValuesProcessed = ref<FormValue>({});
const fields = new Map<string, any>(); const fields = new Map<string, any>();
const requestFuc = getConfig('request') as Function; const requestFuc = getConfig('request') as Function;
@ -79,8 +90,11 @@ const formState: FormState = reactive<FormState>({
popperClass: props.popperClass, popperClass: props.popperClass,
config: props.config, config: props.config,
initValues: props.initValues, initValues: props.initValues,
isCompare: props.isCompare,
lastValues: props.lastValues,
parentValues: props.parentValues, parentValues: props.parentValues,
values, values,
lastValuesProcessed,
$emit: emit as (event: string, ...args: any[]) => void, $emit: emit as (event: string, ...args: any[]) => void,
fields, fields,
setField: (prop: string, field: any) => fields.set(prop, field), setField: (prop: string, field: any) => fields.set(prop, field),
@ -98,6 +112,8 @@ const formState: FormState = reactive<FormState>({
watchEffect(() => { watchEffect(() => {
formState.initValues = props.initValues; formState.initValues = props.initValues;
formState.lastValues = props.lastValues;
formState.isCompare = props.isCompare;
formState.config = props.config; formState.config = props.config;
formState.keyProp = props.keyProp; formState.keyProp = props.keyProp;
formState.popperClass = props.popperClass; formState.popperClass = props.popperClass;
@ -118,8 +134,20 @@ watch(
config: props.config, config: props.config,
}).then((value) => { }).then((value) => {
values.value = 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; initialized.value = true;
}); });
}
}, },
{ immediate: true }, { immediate: true },
); );
@ -130,6 +158,7 @@ const changeHandler = () => {
defineExpose({ defineExpose({
values, values,
lastValuesProcessed,
formState, formState,
initialized, initialized,

View File

@ -2,6 +2,8 @@
<TMagicCol v-show="display && config.type !== 'hidden'" :span="span"> <TMagicCol v-show="display && config.type !== 'hidden'" :span="span">
<Container <Container
:model="model" :model="model"
:lastValues="lastValues"
:is-compare="isCompare"
:config="config" :config="config"
:prop="prop" :prop="prop"
:label-width="config.labelWidth || labelWidth" :label-width="config.labelWidth || labelWidth"
@ -9,6 +11,7 @@
:size="size" :size="size"
:disabled="disabled" :disabled="disabled"
@change="changeHandler" @change="changeHandler"
@add-diff-count="onAddDiffCount"
></Container> ></Container>
</TMagicCol> </TMagicCol>
</template> </template>
@ -25,6 +28,8 @@ import Container from './Container.vue';
const props = defineProps<{ const props = defineProps<{
model: any; model: any;
lastValues?: any;
isCompare?: boolean;
config: ChildConfig; config: ChildConfig;
labelWidth?: string; labelWidth?: string;
expandMore?: boolean; expandMore?: boolean;
@ -34,9 +39,10 @@ const props = defineProps<{
disabled?: boolean; disabled?: boolean;
}>(); }>();
const emit = defineEmits(['change']); const emit = defineEmits(['change', 'addDiffCount']);
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
const display = computed(() => displayFunction(mForm, props.config.display, props)); const display = computed(() => displayFunction(mForm, props.config.display, props));
const changeHandler = () => emit('change', props.model); const changeHandler = () => emit('change', props.model);
const onAddDiffCount = () => emit('addDiffCount');
</script> </script>

View File

@ -20,6 +20,8 @@
:size="size" :size="size"
:is="tagName" :is="tagName"
:model="model" :model="model"
:last-values="lastValues"
:is-compare="isCompare"
:config="config" :config="config"
:disabled="disabled" :disabled="disabled"
:name="name" :name="name"
@ -28,15 +30,120 @@
:expand-more="expand" :expand-more="expand"
:label-width="itemLabelWidth" :label-width="itemLabelWidth"
@change="onChangeHandler" @change="onChangeHandler"
@addDiffCount="onAddDiffCount"
></component> ></component>
<template v-else-if="type && display"> <template v-else-if="type && display && !showDiff">
<TMagicFormItem <TMagicFormItem
:style="config.tip ? 'flex: 1' : ''" :style="config.tip ? 'flex: 1' : ''"
:class="{ hidden: `${itemLabelWidth}` === '0' || !config.text }" :class="{ hidden: `${itemLabelWidth}` === '0' || !config.text }"
:prop="itemProp" :prop="itemProp"
:label-width="itemLabelWidth" :label-width="itemLabelWidth"
:rules="rule" :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> <template #label><span v-html="type === 'checkbox' ? '' : config.text"></span></template>
<TMagicTooltip v-if="tooltip"> <TMagicTooltip v-if="tooltip">
@ -86,6 +193,8 @@
v-for="item in items" v-for="item in items"
:key="key(item)" :key="key(item)"
:model="name || name === 0 ? model[name] : model" :model="name || name === 0 ? model[name] : model"
:last-values="name || name === 0 ? lastValues[name] : lastValues"
:is-compare="isCompare"
:config="item" :config="item"
:size="size" :size="size"
:disabled="disabled" :disabled="disabled"
@ -94,6 +203,7 @@
:label-width="itemLabelWidth" :label-width="itemLabelWidth"
:prop="itemProp" :prop="itemProp"
@change="onChangeHandler" @change="onChangeHandler"
@addDiffCount="onAddDiffCount"
></Container> ></Container>
</template> </template>
</template> </template>
@ -107,8 +217,9 @@
</template> </template>
<script setup lang="ts" name="MFormContainer"> <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 { WarningFilled } from '@element-plus/icons-vue';
import { isEqual } from 'lodash-es';
import { TMagicButton, TMagicFormItem, TMagicIcon, TMagicTooltip } from '@tmagic/design'; import { TMagicButton, TMagicFormItem, TMagicIcon, TMagicTooltip } from '@tmagic/design';
@ -117,7 +228,10 @@ import { display as displayFunction, filterFunction, getRules } from '../utils/f
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
/** 表单值 */
model: FormValue; model: FormValue;
/** 需对比的值(开启对比模式时传入) */
lastValues?: FormValue;
config: ChildConfig; config: ChildConfig;
prop?: string; prop?: string;
disabled?: boolean; disabled?: boolean;
@ -125,15 +239,19 @@ const props = withDefaults(
expandMore?: boolean; expandMore?: boolean;
stepActive?: string | number; stepActive?: string | number;
size?: string; size?: string;
/** 是否开启对比模式 */
isCompare?: boolean;
}>(), }>(),
{ {
prop: '', prop: '',
size: 'small', size: 'small',
expandMore: false, expandMore: false,
lastValues: () => ({}),
isCompare: false,
}, },
); );
const emit = defineEmits(['change']); const emit = defineEmits(['change', 'addDiffCount']);
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
@ -141,6 +259,14 @@ const expand = ref(false);
const name = computed(() => props.config.name || ''); 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 items = computed(() => (props.config as ContainerCommonConfig).items);
const itemProp = computed(() => { const itemProp = computed(() => {
@ -196,6 +322,21 @@ watchEffect(() => {
expand.value = props.expandMore; 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 expandHandler = () => (expand.value = !expand.value);
const key = (config: any) => config[mForm?.keyProps]; 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 onChangeHandler = async function (v: FormValue, key?: string) {
const { filter, onChange, trim, name, dynamicKey } = props.config as any; const { filter, onChange, trim, name, dynamicKey } = props.config as any;
let value: FormValue | number | string = v; let value: FormValue | number | string = v;

View File

@ -24,6 +24,8 @@
v-for="(item, index) in config.items" v-for="(item, index) in config.items"
:key="key(item, index)" :key="key(item, index)"
:model="name ? model[name] : model" :model="name ? model[name] : model"
:lastValues="name ? lastValues[name] : lastValues"
:is-compare="isCompare"
:rules="name ? rules[name] : []" :rules="name ? rules[name] : []"
:config="item" :config="item"
:prop="prop" :prop="prop"
@ -31,6 +33,7 @@
:labelWidth="lWidth" :labelWidth="lWidth"
:size="size" :size="size"
@change="change" @change="change"
@add-diff-count="onAddDiffCount()"
></Container> ></Container>
</div> </div>
@ -42,6 +45,8 @@
v-for="(item, index) in config.items" v-for="(item, index) in config.items"
:key="key(item, index)" :key="key(item, index)"
:model="name ? model[name] : model" :model="name ? model[name] : model"
:lastValues="name ? lastValues[name] : lastValues"
:is-compare="isCompare"
:rules="name ? rules[name] : []" :rules="name ? rules[name] : []"
:config="item" :config="item"
:prop="prop" :prop="prop"
@ -49,6 +54,7 @@
:size="size" :size="size"
:disabled="disabled" :disabled="disabled"
@change="change" @change="change"
@addDiffCount="onAddDiffCount()"
></Container> ></Container>
</template> </template>
</fieldset> </fieldset>
@ -69,6 +75,8 @@ const props = withDefaults(
prop: string; prop: string;
size?: string; size?: string;
model: Record<string, any>; model: Record<string, any>;
lastValues?: Record<string, any>;
isCompare?: boolean;
config: FieldsetConfig; config: FieldsetConfig;
rules?: any; rules?: any;
disabled?: boolean; disabled?: boolean;
@ -76,10 +84,12 @@ const props = withDefaults(
{ {
rules: {}, rules: {},
prop: '', prop: '',
lastValues: () => ({}),
isCompare: false,
}, },
); );
const emit = defineEmits(['change']); const emit = defineEmits(['change', 'addDiffCount']);
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
@ -113,4 +123,5 @@ if (props.config.checkbox && name.value) {
}, },
); );
} }
const onAddDiffCount = () => emit('addDiffCount');
</script> </script>

View File

@ -10,6 +10,8 @@
v-for="(item, index) in model[name]" v-for="(item, index) in model[name]"
:key="index" :key="index"
:model="item" :model="item"
:lastValues="getLastValues(lastValues[name], index)"
:is-compare="isCompare"
:config="config" :config="config"
:prop="prop" :prop="prop"
:index="index" :index="index"
@ -20,6 +22,7 @@
@remove-item="removeHandler" @remove-item="removeHandler"
@swap-item="swapHandler" @swap-item="swapHandler"
@change="changeHandler" @change="changeHandler"
@addDiffCount="onAddDiffCount()"
></MFieldsGroupListItem> ></MFieldsGroupListItem>
<TMagicButton @click="addHandler" size="small" :disabled="disabled" v-if="addable">添加组</TMagicButton> <TMagicButton @click="addHandler" size="small" :disabled="disabled" v-if="addable">添加组</TMagicButton>
@ -41,6 +44,8 @@ import MFieldsGroupListItem from './GroupListItem.vue';
const props = defineProps<{ const props = defineProps<{
model: any; model: any;
lastValues?: any;
isCompare?: boolean;
config: GroupListConfig; config: GroupListConfig;
name: string; name: string;
labelWidth?: string; labelWidth?: string;
@ -49,7 +54,7 @@ const props = defineProps<{
disabled?: boolean; disabled?: boolean;
}>(); }>();
const emit = defineEmits(['change']); const emit = defineEmits(['change', 'addDiffCount']);
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
@ -123,4 +128,7 @@ const toggleMode = () => {
text: null, text: null,
}))) as any; }))) as any;
}; };
const onAddDiffCount = () => emit('addDiffCount');
const getLastValues = (item: any, index: number) => item?.[index] || {};
</script> </script>

View File

@ -32,11 +32,14 @@
v-if="expand" v-if="expand"
:config="rowConfig" :config="rowConfig"
:model="model" :model="model"
:lastValues="lastValues"
:is-compare="isCompare"
:labelWidth="labelWidth" :labelWidth="labelWidth"
:prop="`${prop}${prop ? '.' : ''}${String(index)}`" :prop="`${prop}${prop ? '.' : ''}${String(index)}`"
:size="size" :size="size"
:disabled="disabled" :disabled="disabled"
@change="changeHandler" @change="changeHandler"
@addDiffCount="onAddDiffCount()"
></Container> ></Container>
</div> </div>
</template> </template>
@ -54,6 +57,8 @@ import Container from './Container.vue';
const props = defineProps<{ const props = defineProps<{
model: any; model: any;
lastValues: any;
isCompare?: boolean;
groupModel: any[]; groupModel: any[];
config: GroupListConfig; config: GroupListConfig;
labelWidth?: string; labelWidth?: string;
@ -63,7 +68,7 @@ const props = defineProps<{
disabled?: boolean; 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 mForm = inject<FormState | undefined>('mForm');
const expand = ref(false); const expand = ref(false);
@ -122,4 +127,5 @@ const movable = () => {
} }
return movable; return movable;
}; };
const onAddDiffCount = () => emit('addDiffCount');
</script> </script>

View File

@ -23,11 +23,14 @@
:key="item[mForm?.keyProp || '__key'] ?? index" :key="item[mForm?.keyProp || '__key'] ?? index"
:config="item" :config="item"
:model="name ? model[name] : model" :model="name ? model[name] : model"
:lastValues="name ? lastValues[name] : lastValues"
:is-compare="isCompare"
:prop="prop" :prop="prop"
:size="size" :size="size"
:disabled="disabled" :disabled="disabled"
:label-width="config.labelWidth || labelWidth" :label-width="config.labelWidth || labelWidth"
@change="changeHandler" @change="changeHandler"
@addDiffCount="onAddDiffCount()"
></Container> ></Container>
</div> </div>
@ -40,11 +43,14 @@
:key="item[mForm?.keyProp || '__key'] ?? index" :key="item[mForm?.keyProp || '__key'] ?? index"
:config="item" :config="item"
:model="name ? model[name] : model" :model="name ? model[name] : model"
:lastValues="name ? lastValues[name] : lastValues"
:is-compare="isCompare"
:prop="prop" :prop="prop"
:size="size" :size="size"
:disabled="disabled" :disabled="disabled"
:label-width="config.labelWidth || labelWidth" :label-width="config.labelWidth || labelWidth"
@change="changeHandler" @change="changeHandler"
@addDiffCount="onAddDiffCount()"
></Container> ></Container>
</template> </template>
</div> </div>
@ -64,6 +70,8 @@ import Container from './Container.vue';
const props = defineProps<{ const props = defineProps<{
model: any; model: any;
lastValues?: any;
isCompare?: boolean;
config: PanelConfig; config: PanelConfig;
name: string; name: string;
labelWidth?: string; labelWidth?: string;
@ -72,7 +80,7 @@ const props = defineProps<{
disabled?: boolean; disabled?: boolean;
}>(); }>();
const emit = defineEmits(['change']); const emit = defineEmits(['change', 'addDiffCount']);
const mForm = inject<FormState | undefined>('mForm'); 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 filter = (config: any) => filterFunction(mForm, config, props);
const changeHandler = () => emit('change', props.model); const changeHandler = () => emit('change', props.model);
const onAddDiffCount = () => emit('addDiffCount');
</script> </script>

View File

@ -8,10 +8,13 @@
:labelWidth="config.labelWidth || labelWidth" :labelWidth="config.labelWidth || labelWidth"
:expandMore="expandMore" :expandMore="expandMore"
:model="name ? model[name] : model" :model="name ? model[name] : model"
:lastValues="name ? lastValues[name] : lastValues"
:is-compare="isCompare"
:prop="prop" :prop="prop"
:size="size" :size="size"
:disabled="disabled" :disabled="disabled"
@change="changeHandler" @change="changeHandler"
@add-diff-count="onAddDiffCount"
/> />
</TMagicRow> </TMagicRow>
</template> </template>
@ -27,6 +30,8 @@ import Col from './Col.vue';
const props = defineProps<{ const props = defineProps<{
model: any; model: any;
lastValues?: any;
isCompare?: boolean;
config: RowConfig; config: RowConfig;
name: string; name: string;
labelWidth?: string; labelWidth?: string;
@ -36,9 +41,10 @@ const props = defineProps<{
disabled?: boolean; disabled?: boolean;
}>(); }>();
const emit = defineEmits(['change']); const emit = defineEmits(['change', 'addDiffCount']);
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
const changeHandler = () => emit('change', props.name ? props.model[props.name] : props.model); const changeHandler = () => emit('change', props.name ? props.model[props.name] : props.model);
const onAddDiffCount = () => emit('addDiffCount');
</script> </script>

View File

@ -18,11 +18,14 @@
:key="item[mForm?.keyProp || '__key']" :key="item[mForm?.keyProp || '__key']"
:config="item" :config="item"
:model="step.name ? model[step.name] : model" :model="step.name ? model[step.name] : model"
:lastValues="step.name ? lastValues[step.name] : lastValues"
:is-compare="isCompare"
:prop="`${step.name}`" :prop="`${step.name}`"
:size="size" :size="size"
:disabled="disabled" :disabled="disabled"
:label-width="config.labelWidth || labelWidth" :label-width="config.labelWidth || labelWidth"
@change="changeHandler" @change="changeHandler"
@addDiffCount="onAddDiffCount()"
></Container> ></Container>
</template> </template>
</template> </template>
@ -41,6 +44,8 @@ import Container from './Container.vue';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
model: any; model: any;
lastValues?: any;
isCompare?: boolean;
config: StepConfig; config: StepConfig;
stepActive?: number; stepActive?: number;
labelWidth?: string; 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 mForm = inject<FormState | undefined>('mForm');
const active = ref(1); const active = ref(1);
@ -69,4 +74,5 @@ const stepClick = (index: number) => {
const changeHandler = () => { const changeHandler = () => {
emit('change', props.model); emit('change', props.model);
}; };
const onAddDiffCount = () => emit('addDiffCount');
</script> </script>

View File

@ -9,6 +9,7 @@
style="width: 100%" style="width: 100%"
:row-key="config.rowKey || 'id'" :row-key="config.rowKey || 'id'"
:data="data" :data="data"
:lastData="lastData"
:border="config.border" :border="config.border"
:max-height="config.maxHeight" :max-height="config.maxHeight"
:default-expand-all="true" :default-expand-all="true"
@ -108,8 +109,11 @@
:rules="column.rules" :rules="column.rules"
:config="makeConfig(column, scope.row)" :config="makeConfig(column, scope.row)"
:model="scope.row" :model="scope.row"
:lastValues="lastData[scope.$index]"
:is-compare="isCompare"
:size="size" :size="size"
@change="$emit('change', model[modelName])" @change="$emit('change', model[modelName])"
@addDiffCount="onAddDiffCount()"
></Container> ></Container>
</template> </template>
</TMagicTableColumn> </TMagicTableColumn>
@ -196,6 +200,8 @@ import Container from './Container.vue';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
model: any; model: any;
lastValues?: any;
isCompare?: boolean;
config: TableConfig; config: TableConfig;
name: string; name: string;
prop?: string; prop?: string;
@ -213,10 +219,12 @@ const props = withDefaults(
sortKey: '', sortKey: '',
enableToggleMode: true, enableToggleMode: true,
showIndex: true, showIndex: true,
lastValues: () => ({}),
isCompare: false,
}, },
); );
const emit = defineEmits(['change', 'select']); const emit = defineEmits(['change', 'select', 'addDiffCount']);
let timer: any | null = null; let timer: any | null = null;
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
@ -241,6 +249,15 @@ const data = computed(() =>
: props.model[modelName.value], : 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) => { const sortChange = ({ prop, order }: SortProp) => {
if (order === 'ascending') { if (order === 'ascending') {
props.model[modelName.value] = props.model[modelName.value].sort((a: any, b: any) => a[prop] - b[prop]); 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}`; return `${prop.value}${prop.value ? '.' : ''}${index + 1 + pagecontext.value * pagesize.value - 1}`;
}; };
const onAddDiffCount = () => emit('addDiffCount');
defineExpose({ defineExpose({
toggleRowSelection, toggleRowSelection,
}); });

View File

@ -18,25 +18,26 @@
:label="filter(tab.title)" :label="filter(tab.title)"
:lazy="tab.lazy || false" :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 <Container
v-for="item in tabItems(tab)" v-for="item in tabItems(tab)"
:key="item[mForm?.keyProp || '__key']" :key="item[mForm?.keyProp || '__key']"
:config="item" :config="item"
:disabled="disabled" :disabled="disabled"
:model=" :model="getValues(model, tabIndex, tab)"
config.dynamic :last-values="getValues(lastValues, tabIndex, tab)"
? (name ? model[name] : model)[tabIndex] :is-compare="isCompare"
: tab.name
? (name ? model[name] : model)[tab.name]
: name
? model[name]
: model
"
:prop="config.dynamic ? `${prop}${prop ? '.' : ''}${String(tabIndex)}` : prop" :prop="config.dynamic ? `${prop}${prop ? '.' : ''}${String(tabIndex)}` : prop"
:size="size" :size="size"
:label-width="tab.labelWidth || labelWidth" :label-width="tab.labelWidth || labelWidth"
:expand-more="expandMore" :expand-more="expandMore"
@change="changeHandler" @change="changeHandler"
@addDiffCount="onAddDiffCount(tabIndex)"
></Container> ></Container>
</component> </component>
</template> </template>
@ -54,6 +55,10 @@ import { display as displayFunc, filterFunction } from '../utils/form';
import Container from './Container.vue'; import Container from './Container.vue';
type DiffCount = {
[tabIndex: number]: number;
};
const uiComponent = getConfig('components').tabPane; const uiComponent = getConfig('components').tabPane;
const getActive = (mForm: FormState | undefined, props: any, activeTabName: string) => { 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; model: any;
lastValues?: any;
isCompare?: boolean;
config: TabConfig; config: TabConfig;
name: string; name: string;
size?: string; size?: string;
@ -92,12 +100,18 @@ const props = defineProps<{
prop?: string; prop?: string;
expandMore?: boolean; expandMore?: boolean;
disabled?: boolean; disabled?: boolean;
}>(); }>(),
{
lastValues: () => ({}),
isCompare: false,
},
);
const emit = defineEmits(['change']); const emit = defineEmits(['change', 'addDiffCount']);
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
const activeTabName = ref(getActive(mForm, props, '')); const activeTabName = ref(getActive(mForm, props, ''));
const diffCount = ref<DiffCount>({});
const tabs = computed(() => { const tabs = computed(() => {
if (props.config.dynamic) { if (props.config.dynamic) {
@ -170,4 +184,27 @@ const changeHandler = () => {
props.config.onChange(mForm, { model: props.model, prop: props.prop, config: props.config }); 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;
};
// tabstab
const onAddDiffCount = (tabIndex: number) => {
if (!diffCount.value[tabIndex]) {
diffCount.value[tabIndex] = 1;
} else {
diffCount.value[tabIndex] += 1;
}
//
emit('addDiffCount');
};
</script> </script>

View File

@ -28,6 +28,8 @@ export type FormState = {
config: FormConfig; config: FormConfig;
popperClass?: string; popperClass?: string;
initValues: FormValue; initValues: FormValue;
lastValues: FormValue;
isCompare: boolean;
values: FormValue; values: FormValue;
$emit: (event: string, ...args: any[]) => void; $emit: (event: string, ...args: any[]) => void;
keyProp?: string; keyProp?: string;

View File

@ -7,3 +7,4 @@
@use "./panel.scss"; @use "./panel.scss";
@use "./table.scss"; @use "./table.scss";
@use "./select.scss"; @use "./select.scss";
@use "./tabs.scss";

View File

@ -23,3 +23,7 @@
.magic-form-tab { .magic-form-tab {
margin-bottom: 10px; margin-bottom: 10px;
} }
.diff-count-badge {
top: -10px;
}

View File

@ -1,9 +1,21 @@
<template> <template>
<div style="width: 100%"> <div style="width: 100%; overflow-y: auto">
<nav-menu :data="menu"></nav-menu> <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"> <div class="form-content">
<m-form ref="form" :config="config" :init-values="initValue" size="small" height="100%"></m-form> <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> <magic-code-editor class="code-editor-content" :init-values="config" @save="change"></magic-code-editor>
</div> </div>
</div> </div>
@ -30,6 +42,58 @@ const resultVisible = ref(false);
const result = ref(''); const result = ref('');
const form = ref<InstanceType<typeof MForm>>(); 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([ const config = ref([
{ {
text: '文本', text: '文本',
@ -358,9 +422,16 @@ function change(value: string) {
</script> </script>
<style lang="scss"> <style lang="scss">
.diff-form {
width: 500px;
margin: 20px 0 0 50px;
}
.title {
margin: 20px 0 0 50px;
}
.form-content { .form-content {
display: flex; display: flex;
height: calc(100% - 75px); height: 800px;
.code-editor-content, .code-editor-content,
.m-form { .m-form {