@@ -117,23 +117,11 @@ export default {
inputValue: this.value,
oldValue: this.value, // 记录上一次的值
showModal: false, // 控制模态框显示
- modificationRecords: [
- {},
- {},
- {},
- {},
- {},
- {},
- {},
- {},
- {},
- {},
- {},
- {},
- ], // 存储修改记录
+ modificationRecords: [], // 存储修改记录
modalTimer: null, // 用于延迟隐藏模态框
isHoveringModal: false, // 是否悬停在模态框上
isHoveringMain: false, // 是否悬停在主元素上(这个实际上不需要,因为我们有事件处理)
+ db: null, // IndexedDB 实例
}
},
watch: {
@@ -147,13 +135,6 @@ export default {
},
methods: {
- // 鼠标移入事件
- onHover() {
-
- },
- // 鼠标移出事件
- onLeave() {
- },
getFillTypeStyle(type) {
const {fillType} = this.item;
const typeObj = {
@@ -186,6 +167,75 @@ export default {
this.$emit('update:error', true);
}
},
+ async onCommonHandleSaveRecord(val){
+ const isEmpty = this.isValueEmpty(this.inputValue);
+ if (this.error && !isEmpty) {
+ this.$emit('update:error', false);
+ } else if (!this.error && isEmpty) {
+ this.$emit('update:error', true);
+ }
+ const {templateStatus} = this.$store.state.template;
+ // 检查值是否发生变化
+ if (this.inputValue !== this.oldValue && templateStatus === "actFill") {
+ // 值发生了变化,需要弹出密码输入框
+ try {
+ // const passwordResult = await this.$prompt('请输入密码以确认修改', '密码验证', {
+ // confirmButtonText: '确定',
+ // cancelButtonText: '取消',
+ // inputType: 'password',
+ // inputPattern: /.+/,
+ // inputErrorMessage: '请输入密码',
+ // zIndex: 10000,
+ // });
+ // 用户输入密码并点击确定,保存修改
+ this.oldValue = this.inputValue; // 更新旧值
+ this.$emit("blur", val);
+ this.$emit("change", val);
+ // 调用后端接口记录修改记录
+ await this.saveModificationRecord();
+ } catch {
+ // 用户点击取消,还原数据
+ this.inputValue = this.oldValue;
+ this.$emit('input', this.oldValue); // 触发 v-model 更新
+ this.$emit("blur", this.oldValue);
+ this.$emit("change", this.oldValue);
+ }
+ } else {
+ // 值没有变化,正常触发 blur和change 事件
+ this.$emit("blur", val)
+ this.$emit("change", val)
+ }
+ },
+ // 通用的值变化处理方法
+ async handleValueChange(val, componentType = '') {
+ const oldValue = this.oldValue; // 保存旧值
+ this.$emit('input', val);
+ this.$emit('change', val);
+
+ // 根据输入值判断是否显示错误状态
+ const isEmpty = this.isValueEmpty(val);
+ if (this.error && !isEmpty) {
+ this.$emit('update:error', false);
+ } else if (!this.error && isEmpty) {
+ this.$emit('update:error', true);
+ }
+
+ // 值发生改变,记录修改
+ const { templateStatus } = this.$store.state.template;
+ if (val !== oldValue && templateStatus === "actFill") {
+ // 值发生了变化,记录修改
+ try {
+ this.oldValue = val; // 更新旧值
+
+ // 调用后端接口记录修改记录
+ await this.saveModificationRecord();
+ } catch (error) {
+ const componentName = componentType || '组件';
+ console.error(`记录${componentName}修改失败:`, error);
+ }
+ }
+ },
+
// 判断值是否为空
isValueEmpty(value) {
if (value === null || value === undefined || value === '') {
@@ -215,9 +265,8 @@ export default {
isShowHandle() {
const { fillType } = this.item;
const { templateStatus } = this.$store.state.template;
- return true;
//只有当模板状态是qc和实际填报时,才显示操作按钮
- // return (templateStatus === "qc" || templateStatus === "actFill") && fillType === "actFill";
+ return (templateStatus === "qc" || templateStatus === "actFill") && fillType === "actFill";
},
//判断是否禁用
getDisabled() {
@@ -255,92 +304,213 @@ export default {
onCopy() {
this.$emit("copy")
},
- async onBlur(val) {
- // 根据输入值判断是否显示错误状态
- const isEmpty = this.isValueEmpty(this.inputValue);
- if (this.error && !isEmpty) {
- this.$emit('update:error', false);
- } else if (!this.error && isEmpty) {
- this.$emit('update:error', true);
+
+ // 记录数据修改
+
+
+ // 鼠标进入主容器
+
+
+ // 初始化 IndexedDB
+ initDB() {
+ return new Promise((resolve, reject) => {
+ const request = indexedDB.open('ModificationRecordsDB', 1);
+
+ request.onerror = (event) => {
+ console.error('IndexedDB error:', event.target.error);
+ reject(event.target.error);
+ };
+
+ request.onsuccess = (event) => {
+ this.db = event.target.result;
+ resolve(this.db);
+ };
+
+ request.onupgradeneeded = (event) => {
+ const db = event.target.result;
+ if (!db.objectStoreNames.contains('modificationRecords')) {
+ const objectStore = db.createObjectStore('modificationRecords', { keyPath: 'id' });
+ objectStore.createIndex('fieldId', 'fieldId', { unique: false });
+ objectStore.createIndex('timestamp', 'timestamp', { unique: false });
+ }
+ };
+ });
+ },
+
+ // 生成唯一字段ID (id + key值)
+ getFieldId() {
+ // 使用写死的id作为示例,后续您可以用实际的id替换
+ const templateId = 'template_123'; // 这里是写死的id
+ const fieldKey = this.item.key || this.item.prop || this.item.label || 'default_key';
+
+ // 考虑到CustomTable组件可能有重复的key值,我们需要额外标识
+ // 如果在表格中,可能需要添加行索引等信息
+ const tableRowIndex = this.item.rowIndex !== undefined ? `_${this.item.rowIndex}` : '';
+
+ return `${templateId}_${fieldKey}${tableRowIndex}`;
+ },
+
+ // 获取 IndexedDB 对象存储实例
+ async getObjectStore(storeName = 'modificationRecords', mode = 'readonly') {
+ if (!this.db) {
+ await this.initDB();
}
+ const transaction = this.db.transaction([storeName], mode);
+ return transaction.objectStore(storeName);
+ },
+
+ // 保存单条修改记录到 IndexedDB
+ async saveRecordToDB(record) {
+ const objectStore = await this.getObjectStore('modificationRecords', 'readwrite');
- // 检查值是否发生变化
- if (this.inputValue !== this.oldValue && this.fillType === "actFill") {
- return;
- // 值发生了变化,需要弹出密码输入框
- try {
- const passwordResult = await this.$prompt('请输入密码以确认修改', '密码验证', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- inputType: 'password',
- inputPattern: /.+/,
- inputErrorMessage: '请输入密码'
+ const fieldId = this.getFieldId();
+ const newRecord = {
+ id: `${fieldId}_${Date.now()}`, // 使用时间戳确保唯一性
+ fieldId: fieldId,
+ oldValue: record.oldValue,
+ newValue: record.newValue,
+ timestamp: new Date().toLocaleString(),
+ };
+
+ return new Promise((resolve, reject) => {
+ const request = objectStore.add(newRecord);
+
+ request.onsuccess = () => {
+ resolve(request.result);
+ };
+
+ request.onerror = (event) => {
+ reject(event.target.error);
+ };
+ });
+ },
+
+ // 从 IndexedDB 获取修改记录
+ async getRecordsFromDB() {
+ if (!this.db) {
+ await this.initDB();
+ }
+
+ const transaction = this.db.transaction(['modificationRecords'], 'readonly');
+ const objectStore = transaction.objectStore('modificationRecords');
+ const fieldIdIndex = objectStore.index('fieldId');
+
+ const fieldId = this.getFieldId();
+
+ return new Promise((resolve, reject) => {
+ const request = fieldIdIndex.getAll(IDBKeyRange.only(fieldId));
+
+ request.onsuccess = (event) => {
+ // 按时间戳排序,最新的在前
+ const records = event.target.result.sort((a, b) => {
+ return new Date(b.timestamp) - new Date(a.timestamp);
});
+ resolve(records);
+ };
+
+ request.onerror = (event) => {
+ reject(event.target.error);
+ };
+ });
+ },
+
+ // 同步后端修改记录到 IndexedDB
+ async syncRecordsToDB(backendRecords) {
+ // 清空当前字段的记录
+ await this.clearFieldRecords();
+
+ // 批量添加后端记录到 IndexedDB
+ const objectStore = await this.getObjectStore('modificationRecords', 'readwrite');
+
+ const fieldId = this.getFieldId();
+
+ return new Promise((resolve, reject) => {
+ let completed = 0;
+ const total = backendRecords.length;
+
+ if (total === 0) {
+ resolve();
+ return;
+ }
+
+ backendRecords.forEach((record) => {
+ // 标准化记录格式以匹配 IndexedDB 存储格式
+ const newRecord = {
+ id: `${fieldId}_${Date.parse(record.timestamp) || Date.now()}`, // 使用时间戳或当前时间戳作为 ID
+ fieldId: fieldId,
+ oldValue: record.oldValue,
+ newValue: record.newValue,
+ timestamp: record.timestamp || new Date().toLocaleString(),
+ password: record.password ? '***' : '' // 不直接存储密码
+ };
- // 用户输入密码并点击确定,保存修改
- this.oldValue = this.inputValue; // 更新旧值
- this.$emit("blur", val);
+ const request = objectStore.add(newRecord);
- // 调用后端接口记录修改记录
- 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)
- }
+ request.onsuccess = () => {
+ completed++;
+ if (completed === total) {
+ resolve();
+ }
+ };
+
+ request.onerror = (event) => {
+ console.error('同步单条记录失败:', event.target.error);
+ completed++;
+ if (completed === total) {
+ resolve(); // 即使有错误也继续,避免单条记录失败影响整体同步
+ }
+ };
+ });
+ });
},
- // 记录数据修改
- 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);
- // }
+
+ // 清空当前字段的记录
+ async clearFieldRecords() {
+ if (!this.db) {
+ await this.initDB();
+ }
- // 添加修改记录到本地存储
- const record = {
- oldValue: this.oldValue,
- newValue: this.inputValue,
- timestamp: new Date().toLocaleString(), // 格式化时间
- password: password ? '***' : '' // 不直接存储密码
- };
+ const transaction = this.db.transaction(['modificationRecords'], 'readwrite');
+ const objectStore = transaction.objectStore('modificationRecords');
+ const fieldIdIndex = objectStore.index('fieldId');
- // 限制记录数量,只保留最近的20条记录
- if (this.modificationRecords.length >= 20) {
- this.modificationRecords.shift(); // 移除最早的记录
- }
- this.modificationRecords.push(record);
+ const fieldId = this.getFieldId();
- // 发送事件告知父组件值已修改
- this.$emit('modification-recorded', {
- field: this.item.label || '',
- oldValue: this.oldValue,
- newValue: this.inputValue,
- password: password
+ return new Promise((resolve, reject) => {
+ const request = fieldIdIndex.openCursor(IDBKeyRange.only(fieldId));
+
+ request.onsuccess = (event) => {
+ const cursor = event.target.result;
+ if (cursor) {
+ cursor.delete(); // 删除匹配的记录
+ cursor.continue();
+ } else {
+ // 所有匹配的记录已删除
+ resolve();
+ }
+ };
+
+ request.onerror = (event) => {
+ reject(event.target.error);
+ };
});
},
// 鼠标进入主容器
- onMouseEnter(event) {
+ async onMouseEnter(event) {
clearTimeout(this.modalTimer);
- this.showModal = true;
- // 计算模态框位置,显示在当前DOM右边
+ // 从 IndexedDB 加载修改记录
+ try {
+ const records = await this.getRecordsFromDB();
+ this.modificationRecords = records;
+ } catch (error) {
+ console.error('获取修改记录失败:', error);
+ this.modificationRecords = [];
+ }
+
+ // 先计算模态框位置,避免闪烁
+ this.showModal = true;
this.$nextTick(() => {
if (this.$refs.modalRef) {
const elementRect = event.target.getBoundingClientRect();
@@ -375,6 +545,33 @@ export default {
this.modalTimer = setTimeout(() => {
this.showModal = false;
}, 300);
+ },
+
+ // 记录数据修改
+ async saveModificationRecord() {
+ // 添加修改记录到本地存储
+ const record = {
+ oldValue: this.oldValue,
+ newValue: this.inputValue,
+ timestamp: new Date().toLocaleString(), // 格式化时间
+ };
+
+ // 保存到 IndexedDB
+ try {
+ await this.saveRecordToDB(record);
+ // 保存成功后更新本地记录(可选,取决于是否需要立即显示)
+ // const records = await this.getRecordsFromDB();
+ // this.modificationRecords = records;
+ } catch (error) {
+ console.error('保存修改记录失败:', error);
+ }
+
+ // 发送事件告知父组件值已修改
+ this.$emit('modification-recorded', {
+ field: this.item.label || '',
+ oldValue: this.oldValue,
+ newValue: this.inputValue,
+ });
}
},
}
@@ -541,6 +738,14 @@ export default {
min-width: 250px;
overflow: hidden;
pointer-events: auto;
+ opacity: 0;
+ transform: scale(0.9);
+ transition: opacity 0.2s ease, transform 0.2s ease;
+}
+
+.modification-modal.show {
+ opacity: 1;
+ transform: scale(1);
}
.modification-modal .modal-content {
diff --git a/src/views/business/comps/template/comps/sp/IndexDBDemo.vue b/src/views/business/comps/template/comps/sp/IndexDBDemo.vue
index f325d41..3e5bd15 100644
--- a/src/views/business/comps/template/comps/sp/IndexDBDemo.vue
+++ b/src/views/business/comps/template/comps/sp/IndexDBDemo.vue
@@ -16,7 +16,7 @@
v-model="form.basicInfo.name"
placeholder="请输入姓名"
@blur="onFieldChange('basicInfo.name', $event)"
- @focus="loadTooltip('basicInfo.name')"
+ @mouseenter="loadTooltip('basicInfo.name')"
@mouseleave="hideTooltip"
/>
@@ -36,7 +36,7 @@
v-model="form.contact.email"
placeholder="请输入邮箱"
@blur="onFieldChange('contact.email', $event)"
- @focus="loadTooltip('contact.email')"
+ @mouseenter="loadTooltip('contact.email')"
@mouseleave="hideTooltip"
/>
@@ -57,7 +57,7 @@
placeholder="请输入年龄"
type="number"
@blur="onFieldChange('basicInfo.age', $event)"
- @focus="loadTooltip('basicInfo.age')"
+ @mouseenter="loadTooltip('basicInfo.age')"
@mouseleave="hideTooltip"
/>
@@ -197,7 +197,7 @@ export default {
},
async loadTooltip(fieldPath) {
this.isTooltipVisible = false; // 先禁用,避免闪烁
- console.log("lofo")
+ console.log()
const logs = await this.loadLogs(fieldPath);
if (logs.length === 0) {
this.currentTooltipContent = '
暂无修改记录
';
diff --git a/src/views/business/comps/template/comps/sp/SWYPFXRYPZB.vue b/src/views/business/comps/template/comps/sp/SWYPFXRYPZB.vue
index 7c5398f..e36dae5 100644
--- a/src/views/business/comps/template/comps/sp/SWYPFXRYPZB.vue
+++ b/src/views/business/comps/template/comps/sp/SWYPFXRYPZB.vue
@@ -257,9 +257,9 @@ export default {
return await this.validFormFields(["baseInfoRef", "storageConditionRef","stepFormPackageRef","stepRef","remarkRef"]);
},
async onSave() {
- // const formData = await this.getFormData();
+ const formData = await this.getFormData();
- // console.log(formData, "formData")
+ console.log(formData, "formData")
},
}
diff --git a/src/views/business/comps/template/mixins/templateMixin.js b/src/views/business/comps/template/mixins/templateMixin.js
index 811d4e8..0fedd5f 100644
--- a/src/views/business/comps/template/mixins/templateMixin.js
+++ b/src/views/business/comps/template/mixins/templateMixin.js
@@ -31,8 +31,8 @@ export default {
}
},
mounted() {
- // this.setTemplateStatus("actFill");
- this.setTemplateStatus(this.fillType);
+ this.setTemplateStatus("actFill");
+ // this.setTemplateStatus(this.fillType);
},
unmounted() {
this.setTemplateStatus("");