Browse Source

feat:[模板管理][blur事件记录输入的值]

master
luojie 2 days ago
parent
commit
0d415e9a08
4 changed files with 325 additions and 5 deletions
  1. +59
    -3
      src/components/Template/HandleFormItem.vue
  2. +3
    -2
      src/views/business/comps/template/TemplateTable.vue
  3. +261
    -0
      src/views/business/comps/template/comps/sp/IndexDBDemo.vue
  4. +2
    -0
      src/views/business/comps/template/mixins/templateMixin.js

+ 59
- 3
src/components/Template/HandleFormItem.vue View File

@ -84,7 +84,8 @@ export default {
},
data() {
return {
inputValue: this.value
inputValue: this.value,
oldValue: this.value, //
}
},
watch: {
@ -198,7 +199,7 @@ export default {
onCopy() {
this.$emit("copy")
},
onBlur(val) {
async onBlur(val) {
//
const isEmpty = this.isValueEmpty(this.inputValue);
if (this.error && !isEmpty) {
@ -206,8 +207,63 @@ export default {
} else if (!this.error && isEmpty) {
this.$emit('update:error', true);
}
this.$emit("blur", val)
//
if (this.inputValue !== this.oldValue && this.fillType === "actFill") {
return;
//
try {
const passwordResult = await this.$prompt('请输入密码以确认修改', '密码验证', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputType: 'password',
inputPattern: /.+/,
inputErrorMessage: '请输入密码'
});
//
this.oldValue = this.inputValue; //
this.$emit("blur", val);
//
await this.saveModificationRecord(passwordResult.value);
} catch {
//
this.inputValue = this.oldValue;
this.$emit('input', this.oldValue); // v-model
this.$emit("blur", this.oldValue);
}
} else {
// blur
this.$emit("blur", val)
}
},
//
async saveModificationRecord(password) {
// API
//
// const recordData = {
// field: this.item.label || '', //
// oldValue: this.oldValue, //
// newValue: this.inputValue, //
// timestamp: Date.now(), //
// password: password //
// };
// try {
// await api.recordModification(recordData);
// } catch (error) {
// this.$message.error('');
// console.error(':', error);
// }
//
this.$emit('modification-recorded', {
field: this.item.label || '',
oldValue: this.oldValue,
newValue: this.inputValue,
password: password
});
}
},
}
</script>

+ 3
- 2
src/views/business/comps/template/TemplateTable.vue View File

@ -15,6 +15,7 @@ import SWYPFXCBYPZB from "./comps/sp/SWYPFXCBYPZB.vue";
import SWYPBQGZYZBB from "./comps/sp/SWYPBQGZYZBB.vue";
import SWYPNBGZYZBB from "./comps/sp/SWYPNBGZYZBB.vue";
import Demo from "./comps/sp/Demo.vue";
import IndexDBDemo from "./comps/sp/IndexDBDemo.vue";
//
import SYWZPZJHB from "./comps/gy/SYWZPZJHB.vue";
import MJYLQSQD from "./comps/gy/MJYLQSQD.vue";
@ -23,7 +24,7 @@ export default {
name: "TemplateTable",
components: {
MJYLQSQD,SYWZPZJHB,
SP001,SWYPFXRYPZB ,Demo,SWYPFXCBYPZB,SWYPBQGZYZBB,SWYPNBGZYZBB,
SP001,SWYPFXRYPZB ,Demo,SWYPFXCBYPZB,SWYPBQGZYZBB,SWYPNBGZYZBB,IndexDBDemo
},
props: {
sn: {
@ -50,7 +51,7 @@ export default {
'SP004': 'SWYPNBGZYZBB',
'SYWZPZJHB': 'SYWZPZJHB',
'MJYLQSQD': 'MJYLQSQD',
// 'SP001': 'Demo',
// 'SP001': 'IndexDBDemo',
}
}
return this.componentMap || "Demo"

+ 261
- 0
src/views/business/comps/template/comps/sp/IndexDBDemo.vue View File

@ -0,0 +1,261 @@
<template>
<div style="padding: 20px; max-width: 600px;">
<h2>用户信息表单带修改记录</h2>
<el-form ref="formRef" :model="form" label-width="120px">
<!-- 姓名 -->
<el-form-item label="姓名">
<el-tooltip
:content="currentTooltipContent"
placement="top"
effect="light"
:disabled="!isTooltipVisible"
raw-content
popper-class="audit-tooltip"
>
<el-input
v-model="form.basicInfo.name"
placeholder="请输入姓名"
@blur="onFieldChange('basicInfo.name', $event)"
@focus="loadTooltip('basicInfo.name')"
@mouseleave="hideTooltip"
/>
</el-tooltip>
</el-form-item>
<!-- 邮箱 -->
<el-form-item label="邮箱">
<el-tooltip
:content="currentTooltipContent"
placement="top"
effect="light"
:disabled="!isTooltipVisible"
raw-content
popper-class="audit-tooltip"
>
<el-input
v-model="form.contact.email"
placeholder="请输入邮箱"
@blur="onFieldChange('contact.email', $event)"
@focus="loadTooltip('contact.email')"
@mouseleave="hideTooltip"
/>
</el-tooltip>
</el-form-item>
<!-- 年龄 -->
<el-form-item label="年龄">
<el-tooltip
:content="currentTooltipContent"
placement="top"
effect="light"
:disabled="!isTooltipVisible"
raw-content
popper-class="audit-tooltip"
>
<el-input
v-model.number="form.basicInfo.age"
placeholder="请输入年龄"
type="number"
@blur="onFieldChange('basicInfo.age', $event)"
@focus="loadTooltip('basicInfo.age')"
@mouseleave="hideTooltip"
/>
</el-tooltip>
</el-form-item>
<!-- 调试显示所有日志可选 -->
<el-button size="small" @click="showAllLogs">查看所有本地日志控制台</el-button>
</el-form>
</div>
</template>
<script>
export default {
name: 'AuditFormDemo',
data() {
return {
form: {
basicInfo: {
name: '',
age: null
},
contact: {
email: ''
}
},
commonContent: '',
//
initialValues: {
'basicInfo.name': '',
'contact.email': '',
'basicInfo.age': null
},
// hover DB
cachedLogs: {},
cachedLogs: {},
currentTooltipContent: '', // tooltip
isTooltipVisible: false, // tooltip
db: null
};
},
async created() {
await this.initDB();
// DB 稿
},
methods: {
// ========== IndexedDB ==========
initDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('AuditLogDB', 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve();
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('fieldLogs')) {
const store = db.createObjectStore('fieldLogs', { keyPath: 'fieldPath' });
store.createIndex('byField', 'fieldPath', { unique: false });
}
};
});
},
// IndexedDB
async saveLog(fieldPath, oldValue, newValue) {
if (oldValue === newValue) return;
const logEntry = {
fieldPath,
logs: [
{
oldValue,
newValue,
timestamp: new Date().toISOString(),
operator: '当前用户' //
},
...(this.cachedLogs[fieldPath] || [])
].slice(0, 10) // 10
};
const transaction = this.db.transaction(['fieldLogs'], 'readwrite');
const store = transaction.objectStore('fieldLogs');
store.put(logEntry);
//
this.$set(this.cachedLogs, fieldPath, logEntry.logs);
},
// IndexedDB
async loadLogs(fieldPath) {
if (this.cachedLogs[fieldPath]) {
return this.cachedLogs[fieldPath];
}
return new Promise((resolve) => {
const transaction = this.db.transaction(['fieldLogs'], 'readonly');
const store = transaction.objectStore('fieldLogs');
const request = store.get(fieldPath);
request.onsuccess = () => {
const result = request.result?.logs || [];
this.$set(this.cachedLogs, fieldPath, result);
resolve(result);
};
request.onerror = () => {
console.error('读取日志失败:', request.error);
resolve([]);
};
});
},
// ========== ==========
onFieldChange(fieldPath, e) {
console.log(newValue,fieldPath)
const newValue = e.target.value
// cachedLogs oldValue initialValues
let oldValue;
if (this.cachedLogs[fieldPath]?.length > 0) {
oldValue = this.cachedLogs[fieldPath][0].oldValue;
} else {
oldValue = this.initialValues[fieldPath];
}
//
this.saveLog(fieldPath, oldValue, newValue);
},
// ========== Tooltip ==========
hasLogs(fieldPath) {
return (this.cachedLogs[fieldPath]?.length || 0) > 0;
},
async loadTooltip(fieldPath) {
this.isTooltipVisible = false; //
console.log("lofo")
const logs = await this.loadLogs(fieldPath);
if (logs.length === 0) {
this.currentTooltipContent = '<div class="empty">暂无修改记录</div>';
} else {
this.currentTooltipContent = logs
.map(
(log) =>
`<div class="log-item"><strong>${this.formatTime(
log.timestamp
)}</strong><br/>${log.operator} 将值从 "<code>${this.escapeHtml(
String(log.oldValue)
)}</code>" 改为 "<code>${this.escapeHtml(String(log.newValue))}</code>"</div>`
)
.join('');
}
this.isTooltipVisible = true; //
},
hideTooltip() {
this.isTooltipVisible = false;
},
formatTime(isoString) {
return new Date(isoString).toLocaleString('zh-CN');
},
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
},
//
async showAllLogs() {
const transaction = this.db.transaction(['fieldLogs'], 'readonly');
const store = transaction.objectStore('fieldLogs');
const request = store.getAll();
request.onsuccess = () => {
console.log('所有字段修改记录:', request.result);
};
}
}
};
</script>
<style scoped>
.empty {
color: #999;
font-style: italic;
}
.log-item {
margin: 6px 0;
line-height: 1.4;
}
.log-item code {
background: #f5f5f5;
padding: 2px 4px;
border-radius: 3px;
}
</style>

+ 2
- 0
src/views/business/comps/template/mixins/templateMixin.js View File

@ -14,9 +14,11 @@ export default {
handler(v) {
if (v) {
let n = { ...v };
this.formData =n;
if (v.bdnr) {
this.formData = { ...n, ...JSON.parse(v.bdnr) };
}
this.setTemplateData(n);
}
}

Loading…
Cancel
Save