最近写了一个表格设计组件,用户可以通过右侧的属性面板,设置表格行数,列数,调整单元格填充色、边框线颜色、文本颜色。还可以直接操作单元格,插入行列,删除行列,合并拆分单元格,清空内容以及样式。
然而在这样一个复杂的操作过程中,很有可能存在一些误操作或者调整效果不满意,想要撤销设置的效果。针对一些简单的效果还好,再重新设置下就能到达撤销效果,那如果是无法手动撤销的效果,或者说撤销的步骤比较多,操作繁琐。这样用户体验感就下降了。所以,手动实现一个撤销、还原操作是非常有必要的。
基本实现思路:
1、准备一个储存历史记录的数组。
2、记录当前状态数据的索引。
3、初始打开时,将数据存入历史记录数组中,设置索引位置。
4、每操作一步,先删除历史记录数组索引位置之后的数据,再向历史记录数组末尾添加一条数据,并设置索引位置为数组最后一个元素。
5、当前历史记录数组长度到底一定值,删除数组第一个元素保证长度不超过指定值。
6、执行撤销操作时,将索引位置向前移动,再取出数据,进行表格渲染(业务操作)。
7、执行还原操作是,将索引位置向后移动,再取出数据,进行表格渲染(业务操作)。
难点以及解决方式:
1、何时触发添加历史记录操作?
当表格数据发生变化时触发,且不是由撤销、还原操作引发的数据变化时
2、如何实现短时间内的操作视作一步变化?
记录历史记录数据时,额外增加时间毫秒数,当下一次触发变化时,如果当前时间与最后一次记录时间间隔过短,则直接覆盖最后一条历史记录
3、如何实现快捷键监听?
使用widow.document.addEventListener添加事件监听,监听keydown事件,需要注意的是,一定要在销毁组件时删除事件监听,否则会出现多次调用。
演示地址:https://www.zjh336.cn/generalTable#/demo
代码:
data(){
tableJsonOperateArray: [], // 表格json操作数组 用作表格操作历史记录
operateIndex: -1, // 操作记录索引 用于标识在历史记录中的索引
operateHistoryDataFlag: false, // 操作历史记录数据标志
},
method(){
// 表格json对象变化 触发
tableJsonChange({ oldTableJsonObj, newTableJsonObj }) {
// 如果操作历史记录标志为true则不进行后续操作
if (this.operateHistoryDataFlag) {
return
}
// 操作记录索引不等于数组的最后一个下标 需要销毁该索引之后的记录
if (this.operateIndex !== this.tableJsonOperateArray.length - 1) {
// 删除的索引 操作记录索引+1
const deleteIndex = this.operateIndex + 1
// 删除的长度 总长度 - (索引下标+1)
const deleteLen = this.tableJsonOperateArray.length - (this.operateIndex + 1)
// 销毁当前索引之后的历史记录
this.tableJsonOperateArray.splice(deleteIndex, deleteLen)
}
const curDate = new Date()
// 操作记录数据
const operateData = {
historyTime: curDate.getTime(), // 历史记录时间毫秒数
historyTimeFormat: this.$date.formatDate(curDate, 'yyyy-MM-dd hh:mm:ss'), // 历史记录时间格式化
preSecond: 0, // 距离上一次历史记录间隔秒数
tableJson: JSON.stringify(newTableJsonObj) // 表格json字符串
}
// 取数组最后一条数据
const lastData = this.tableJsonOperateArray.length > 0 ? this.tableJsonOperateArray[this.tableJsonOperateArray.length - 1] : null
// 获取历史记录时间毫秒数
const historyTime = lastData ? lastData.historyTime : 0
// 获取当前时间毫秒数
const curDateTime = new Date().getTime()
// 相差秒数
const subSecond = (curDateTime - historyTime) / 1000
// 设置间隔秒数
operateData.preSecond = subSecond
// 超过1秒
if (subSecond > 1) {
// 向后插入数据
this.tableJsonOperateArray.push(operateData)
// 不足1秒
} else {
// 更新最后一条数据
this.$set(this.tableJsonOperateArray, this.tableJsonOperateArray.length - 1, operateData)
}
// 如果长度 大于50
if (this.tableJsonOperateArray.length > 50) {
// 删除第一个元素
this.tableJsonOperateArray.shift()
}
// 设置操作记录索引为数组最后一个下标
this.operateIndex = this.tableJsonOperateArray.length - 1
},
// 操作历史记录 operateType 操作类型 撤销(repeal) 或者 还原(restore)
operateHistory(operateType) {
// 操作历史记录必须保证 历史记录数组有数据
if (!this.tableJsonOperateArray.length) {
// 无数据直接返回
return
}
// 撤销操作且当前历史记录索引等于0
// 或者 还原操作且当前列数记录索引等于最后一位
if ((operateType === 'repeal' && this.operateIndex === 0) ||
(operateType === 'restore' && this.operateIndex === this.tableJsonOperateArray.length - 1)) {
// 返回
return
}
// 开启操作历史记录标志
this.operateHistoryDataFlag = true
// 撤销
if (operateType === 'repeal') {
// 操作索引前移
this.operateIndex--
// 还原
} else if (operateType === 'restore') {
// 操作索引后移
this.operateIndex++
}
// 取出历史记录数据
const historyData = this.tableJsonOperateArray[this.operateIndex]
// 获取表格json数据
const tableJson = historyData.tableJson
// 转换tableJson对象
const tableJsonObj = JSON.parse(tableJson)
this.$refs.tableProperties.tableProperty = JSON.parse(JSON.stringify(this.tableOptions))
this.$refs.tableProperties.tablePropertyCopy = JSON.parse(JSON.stringify(this.tableOptions))
// 刷新表格
this.getTableRef().refreshTable(tableJson)
// 获取表格配置信息
this.tableOptions = tableJsonObj.options
// 获取表格扩展属性值
const extendOptions = tableJsonObj.extendOptions
// 获取预览主题色编码
const previewThemeColorCode = extendOptions ? extendOptions.previewThemeColorCode : ''
// 预览主题色不为空
if (previewThemeColorCode) {
// 触发修改主题色方法
this.$EventBus.$emit('updatePreviewThemeColorCode', previewThemeColorCode)
}
// 触发修改配置模式方法
this.$EventBus.$emit('configModeChange', this.tableOptions.configMode)
// 刷新业务数据
this.$EventBus.$emit('refreshServiceData', {
data: tableJsonObj.data
})
this.$forceUpdate()
setTimeout(() => {
this.operateHistoryDataFlag = false
}, 0)
},
keydownFun(event) {
// 撤销方法 当前为编辑模式
if (event.ctrlKey && event.keyCode === 90 && !this.showPreview) {
this.$nextTick(() => {
this.$refs.generalTableDesignRef.operateHistory('repeal')
})
event.preventDefault()
// 还原方法 当前为编辑模式
} else if (event.ctrlKey && event.keyCode === 89 && !this.showPreview) {
this.$nextTick(() => {
this.$refs.generalTableDesignRef.operateHistory('restore')
})
event.preventDefault()
}
}
},
mounted() {
// 监听键盘事件
window.document.addEventListener('keydown', this.keydownFun, false)
},
destroyed() {
window.document.removeEventListener('keydown', this.keydownFun)
}


发表评论