|
|
|
@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
|
|
<div class="p-5 space-y-5 !bg-gray-100">
|
|
|
|
|
|
|
|
<el-card header="表单变更追踪示例 (30个字段,监控其中6个)">
|
|
|
|
|
|
|
|
<template #header>
|
|
|
|
|
|
|
|
<div class="flex items-center justify-between">
|
|
|
|
|
|
|
|
<span>表单变更追踪示例 (30个字段,监控其中6个)</span>
|
|
|
|
|
|
|
|
<el-tag :type="isModifiedFlag ? 'danger' : 'success'">
|
|
|
|
|
|
|
|
{{ isModifiedFlag ? "敏感字段已修改" : "未检测到敏感修改" }}
|
|
|
|
|
|
|
|
</el-tag>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<el-alert
|
|
|
|
|
|
|
|
title="说明:下方的 [字段1] 到 [字段6] 是被监控的敏感字段。只要它们被修改,右上角的标记就会变红,直到点击保存。"
|
|
|
|
|
|
|
|
type="info"
|
|
|
|
|
|
|
|
:closable="false"
|
|
|
|
|
|
|
|
class="mb-4"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<el-form :model="formData" label-width="100px" label-position="left">
|
|
|
|
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
|
|
|
|
|
|
<!-- 敏感字段 1-6 -->
|
|
|
|
|
|
|
|
<el-form-item
|
|
|
|
|
|
|
|
v-for="i in 6"
|
|
|
|
|
|
|
|
:key="'field' + i"
|
|
|
|
|
|
|
|
:label="`字段${i}(监控)`"
|
|
|
|
|
|
|
|
:class="{ 'is-sensitive': true }"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
|
|
v-model="formData['field' + i]"
|
|
|
|
|
|
|
|
placeholder="修改此项会触发标记"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 其他字段 7-15 (Input) -->
|
|
|
|
|
|
|
|
<el-form-item
|
|
|
|
|
|
|
|
v-for="i in 9"
|
|
|
|
|
|
|
|
:key="'field' + (i + 6)"
|
|
|
|
|
|
|
|
:label="`字段${i + 6}`"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
|
|
v-model="formData['field' + (i + 6)]"
|
|
|
|
|
|
|
|
placeholder="修改此项不触发标记"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 其他字段 16-20 (Select) -->
|
|
|
|
|
|
|
|
<el-form-item
|
|
|
|
|
|
|
|
v-for="i in 5"
|
|
|
|
|
|
|
|
:key="'field' + (i + 15)"
|
|
|
|
|
|
|
|
:label="`字段${i + 15}`"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<el-select
|
|
|
|
|
|
|
|
v-model="formData['field' + (i + 15)]"
|
|
|
|
|
|
|
|
class="w-full"
|
|
|
|
|
|
|
|
placeholder="请选择"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<el-option label="选项A" value="A" />
|
|
|
|
|
|
|
|
<el-option label="选项B" value="B" />
|
|
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 其他字段 21-25 (Radio) -->
|
|
|
|
|
|
|
|
<el-form-item
|
|
|
|
|
|
|
|
v-for="i in 5"
|
|
|
|
|
|
|
|
:key="'field' + (i + 20)"
|
|
|
|
|
|
|
|
:label="`字段${i + 20}`"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<el-radio-group v-model="formData['field' + (i + 20)]">
|
|
|
|
|
|
|
|
<el-radio label="1">状态1</el-radio>
|
|
|
|
|
|
|
|
<el-radio label="2">状态2</el-radio>
|
|
|
|
|
|
|
|
</el-radio-group>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 其他字段 26-30 (Switch/Date) -->
|
|
|
|
|
|
|
|
<el-form-item
|
|
|
|
|
|
|
|
v-for="i in 5"
|
|
|
|
|
|
|
|
:key="'field' + (i + 25)"
|
|
|
|
|
|
|
|
:label="`字段${i + 25}`"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<el-switch
|
|
|
|
|
|
|
|
v-if="i % 2 === 0"
|
|
|
|
|
|
|
|
v-model="formData['field' + (i + 25)]"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<el-date-picker
|
|
|
|
|
|
|
|
v-else
|
|
|
|
|
|
|
|
v-model="formData['field' + (i + 25)]"
|
|
|
|
|
|
|
|
type="date"
|
|
|
|
|
|
|
|
class="!w-full"
|
|
|
|
|
|
|
|
placeholder="选择日期"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="mt-8 flex justify-center space-x-4">
|
|
|
|
|
|
|
|
<el-button @click="resetForm">重置所有字段</el-button>
|
|
|
|
|
|
|
|
<el-button type="primary" @click="handleSave">保存修改</el-button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<el-card header="当前敏感字段快照与对比">
|
|
|
|
|
|
|
|
<div class="grid grid-cols-2 gap-4 text-sm">
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<p class="font-bold mb-2 text-blue-600">上次保存的快照:</p>
|
|
|
|
|
|
|
|
<pre class="bg-gray-50 p-3 rounded border">{{
|
|
|
|
|
|
|
|
JSON.stringify(lastSavedSnapshot, null, 2)
|
|
|
|
|
|
|
|
}}</pre>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<p class="font-bold mb-2 text-green-600">实时计算属性 (isDirty):</p>
|
|
|
|
|
|
|
|
<div class="bg-gray-50 p-3 rounded border h-[135px] flex items-center justify-center">
|
|
|
|
|
|
|
|
<span class="text-lg font-mono" :class="isDirty ? 'text-red-500' : 'text-gray-500'">
|
|
|
|
|
|
|
|
{{ isDirty ? "TRUE (检测到差异)" : "FALSE (与快照一致)" }}
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
|
|
import { ref, reactive, computed, watch, onMounted } from "vue";
|
|
|
|
|
|
|
|
import { message } from "@/utils/message";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 初始化 30 个字段的表单数据
|
|
|
|
|
|
|
|
const formData = reactive<Record<string, any>>({});
|
|
|
|
|
|
|
|
for (let i = 1; i <= 30; i++) {
|
|
|
|
|
|
|
|
// 初始化默认值
|
|
|
|
|
|
|
|
if (i <= 20) formData[`field${i}`] = "";
|
|
|
|
|
|
|
|
else if (i <= 25) formData[`field${i}`] = "1";
|
|
|
|
|
|
|
|
else if (i % 2 === 0) formData[`field${i}`] = false;
|
|
|
|
|
|
|
|
else formData[`field${i}`] = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 定义敏感字段列表 (6个)
|
|
|
|
|
|
|
|
const SENSITIVE_FIELDS = ["field1", "field2", "field3", "field4", "field5", "field6"];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 上次保存的快照 (仅记录敏感字段)
|
|
|
|
|
|
|
|
const lastSavedSnapshot = ref<Record<string, any>>({});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 标记位:只要曾经修改过就为 true,直到点击保存
|
|
|
|
|
|
|
|
const isModifiedFlag = ref(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化快照
|
|
|
|
|
|
|
|
const updateSnapshot = () => {
|
|
|
|
|
|
|
|
const snapshot: Record<string, any> = {};
|
|
|
|
|
|
|
|
SENSITIVE_FIELDS.forEach(field => {
|
|
|
|
|
|
|
|
snapshot[field] = formData[field];
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
lastSavedSnapshot.value = snapshot;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
|
|
updateSnapshot();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 【Computed】优雅地实时对比当前值与快照
|
|
|
|
|
|
|
|
const isDirty = computed(() => {
|
|
|
|
|
|
|
|
return SENSITIVE_FIELDS.some(field => {
|
|
|
|
|
|
|
|
return formData[field] !== lastSavedSnapshot.value[field];
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 【Watch】监听计算属性的变化
|
|
|
|
|
|
|
|
// 当 isDirty 变为 true 时,持久化标记位 isModifiedFlag
|
|
|
|
|
|
|
|
// 即使随后用户把值改回原来的,isModifiedFlag 依然为 true (除非业务逻辑要求改回即复位)
|
|
|
|
|
|
|
|
// 如果业务要求“只要曾经动过就记下”,则如下实现:
|
|
|
|
|
|
|
|
watch(isDirty, newVal => {
|
|
|
|
|
|
|
|
if (newVal) {
|
|
|
|
|
|
|
|
isModifiedFlag.value = true;
|
|
|
|
|
|
|
|
console.log("检测到敏感字段修改,标记已设为 TRUE");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 保存操作
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
const handleSave = () => {
|
|
|
|
|
|
|
|
// 更新快照为当前值
|
|
|
|
|
|
|
|
updateSnapshot();
|
|
|
|
|
|
|
|
// 重置标记位
|
|
|
|
|
|
|
|
isModifiedFlag.value = false;
|
|
|
|
|
|
|
|
message("保存成功,标记已重置", { type: "success" });
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 重置表单
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
const resetForm = () => {
|
|
|
|
|
|
|
|
SENSITIVE_FIELDS.forEach(field => {
|
|
|
|
|
|
|
|
formData[field] = "";
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
// 注意:由于 isDirty 变为 true 会触发 watch,导致 isModifiedFlag 变 true
|
|
|
|
|
|
|
|
// 这是符合“只要有修改”的逻辑的。
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
|
|
.is-sensitive {
|
|
|
|
|
|
|
|
:deep(.el-form-item__label) {
|
|
|
|
|
|
|
|
color: #f56c6c;
|
|
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
</style>
|