roymondchen cbc4b25072 feat(editor): 字段对比模式逐项展示差异并补充历史记录面板文档
- CodeSelect/CodeSelectCol/EventSelect/DataSource 等复合字段在对比模式下
  按索引对齐前后值,逐项展示新增/删除/修改高亮,并隐藏写操作按钮
- form 容器/列表/表格支持对比模式只读展示
- 新增「历史记录面板」指南文档,完善表单对比文档及 menu props 说明
- 补充相关单元测试

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 15:51:47 +08:00

150 lines
3.5 KiB
Vue

<template>
<div class="m-fields-key-value">
<div v-if="!showCode">
<div class="m-fields-key-value-item" v-for="(item, index) in records" :key="index">
<TMagicInput
placeholder="key"
v-model="records[index][0]"
:disabled="disabled"
:size="size"
@change="keyChangeHandler"
></TMagicInput>
<span class="m-fields-key-value-delimiter">:</span>
<TMagicInput
placeholder="value"
v-model="records[index][1]"
:disabled="disabled"
:size="size"
@change="valueChangeHandler"
></TMagicInput>
<TMagicButton
v-if="!isCompare"
class="m-fields-key-value-delete"
type="danger"
:size="size"
:disabled="disabled"
circle
plain
:icon="Delete"
@click="deleteHandler(index)"
></TMagicButton>
</div>
<TMagicButton
v-if="!isCompare"
type="primary"
:size="size"
:disabled="disabled"
plain
:icon="Plus"
@click="addHandler"
>添加</TMagicButton
>
</div>
<MagicCodeEditor
v-if="config.advanced && showCode"
editor-custom-type="m-fields-key-value"
language="javascript"
:init-values="model[name]"
:options="{
readOnly: disabled,
}"
:autosize="{
minRows: 1,
maxRows: 20,
}"
:parse="true"
@save="save"
></MagicCodeEditor>
<TMagicButton
v-if="config.advanced && !isCompare"
size="default"
:disabled="disabled"
link
:icon="CodeIcon"
@click="showCode = !showCode"
></TMagicButton>
</div>
</template>
<script setup lang="ts">
import { computed, inject, ref, watchEffect } from 'vue';
import { Delete, Plus } from '@element-plus/icons-vue';
import { TMagicButton, TMagicInput } from '@tmagic/design';
import type { FieldProps, FormState, KeyValueConfig } from '@tmagic/form';
import CodeIcon from '@editor/icons/CodeIcon.vue';
import MagicCodeEditor from '@editor/layouts/CodeEditor.vue';
defineOptions({
name: 'MFieldsKeyValue',
});
const props = withDefaults(defineProps<FieldProps<KeyValueConfig>>(), {
disabled: false,
});
const emit = defineEmits<{
change: [value: Record<string, any>];
}>();
const mForm = inject<FormState | undefined>('mForm');
/** 对比模式下隐藏增删/代码切换等操作按钮,仅保留只读展示。 */
const isCompare = computed(() => Boolean(mForm?.isCompare));
const records = ref<[string, string][]>([]);
const showCode = ref(false);
watchEffect(() => {
if (typeof props.model[props.name] === 'function') {
showCode.value = true;
} else {
const initValues: [string, any][] = Object.entries(props.model[props.name] || {});
for (const [, value] of initValues) {
if (typeof value !== 'string') {
showCode.value = true;
break;
}
}
records.value = initValues;
}
});
const getValue = () => {
const record: Record<string, string> = {};
records.value.forEach(([key, value]) => {
if (key) {
record[key] = value;
}
});
return record;
};
const keyChangeHandler = () => {
emit('change', getValue());
};
const valueChangeHandler = () => {
emit('change', getValue());
};
const addHandler = () => {
records.value.push(['', '']);
};
const deleteHandler = (index: number) => {
records.value.splice(index, 1);
emit('change', getValue());
};
const save = (v: string | any) => {
emit('change', v);
};
</script>