编辑器:如何查看历史痕迹?

796 阅读4分钟

一、前言

项目是一个后台管理系统,可供用户编写文章。需要查看历史痕迹。

二、前提

  1. 后台模板用的是vue-element-admin,编辑器也是自带的Tinymce编辑器。
  2. 需要下载Google的文件对比模板 google-diff-match-patch

三、原理

根据后端记录的每次修改,将返回的数组数据循环,让前后两个数据两两比较,根据google-diff-match-patch可以将对比结果通过数组的形式返回,记录修改前与修改后的首尾下标,存到一个数组里,后续利用同样的方法,获取到数组数据之后可以跟上一个的得到的数组进行比对,或进行新增、删除、保持不变。

展示一下diff对比后返回的数组数据,如截图

image.png

说一下数组的每一项的含义:

graph TD
下标0 --> 0 --> 未修改 --> =
下标0 --> 1 --> 新增 --> +
下标0 --> -1 --> 删除--> -
下标1 --> 内容

这样我们就可以通过状态以及内容length来获取首尾index,然后就可以做上标记(不变、新增、删除)。

四、简单实现

1.获取前后数组数据

举个栗子: 假如现在数据从12 -> 152再到152 -> 12

首先第一个图是从12 -> 152,对比左右的文本信息,根据google-diff-match-patch的对比情况可得数组

image.png

数组中下标为0的key代表增加(1)、删除(-1)、不变(0) 数组中下标为1的key代表的是内容。

同理图2如下图

image.png

// list是google-diff-match-patch对比之后的数组
// leftIndex:修改前的下标,代表字符串执行到哪里
// rightIndex: 修改后的下标,代表字符串执行到哪里
// sp1:修改前的首下标, sp2:修改后的首下标, ep1:修改前的尾下标, ep2:修改后的尾下标
let leftIndex = 0
let rightindex = 0
let newArray = []
for(let i=0;i<list.length-1;i++) {
  const item = list[i]
  switch(item[0]) {
    // case === 0,说明不变,此时修改前后的index都应该往后加内容的长度
    case 0:
      var sp1 = leftIndex
      var sp2 = rightIndex
      var ep1 = sp1 + item[1].length
      var ep2 = sp2 + item[1].length
      newArray.push({
        type: '=',
        sp1: sp1,
        sp2: sp2,
        ep1: ep1,
        ep2: ep2,
        content: item[1]
      })
      leftIndex = leftIndex + item[1].length
      rightindex = rightindex + item[1].length
      break;
    // case === 1,说明是新增,此时应该修改前的下标不变,修改后的下标加内容的长度
    case 1:
      var sp1 = leftIndex
      var sp2 = rightIndex
      var ep1 = sp1
      var ep2 = sp2 + item[1].length
      newArray.push({
        type: '+',
        sp1: sp1,
        sp2: sp2,
        ep1: ep1,
        ep2: ep2,
        content: item[1]
      })
      rightindex = rightindex + item[1].length
      break;
    // case === -1,说明是删除,此时应该修改前的下标加内容的长度,修改后的下标不变  
    case: -1
      var sp1 = leftIndex
      var sp2 = rightIndex
      var ep1 = sp1 + item[1].length
      var ep2 = sp2
      newArray.push({
        type: '-',
        sp1: sp1,
        sp2: sp2,
        ep1: ep1,
        ep2: ep2,
        content: item[1]
      })
      leftIndex = leftIndex + item[1].length
      break;
    default:
      break;
  }
}

根据上面的代码可得下面两种情况的数据:

下图为第一种情况(=:不变,+:新增,-删除)

(sp1:修改前的首下标, sp2:修改后的首下标, ep1:修改前的尾下标, ep2:修改后的尾下标,尾下标 = 首下标+内容)

首尾下标 / 修改状态=+=
sp1011
sp2012
ep1112
ep2123

下图为第二种情况(=:不变(0),+:新增(1),-:删除(-1))

(sp1:修改前的首下标, sp2:修改后的首下标, ep1:修改前的尾下标, ep2:修改后的尾下标,尾下标 = 首下标+内容)

首尾下标 / 修改状态=-=
sp1012
sp2011
ep1123
ep2112

2.对数组数据进行循环对比

获取到的数组数据经过变形之后大概长这样

  第一个数组数据
  const preArray = [{
    type: '=',
    sp1: 0,
    sp2: 0,
    ep1: 1,
    ep2: 1,
    content: '1'
  }, {
    type: '+',
    sp1: 1,
    sp2: 1,
    ep1: 1,
    ep2: 2,
    content: '5'
  }, {
    type: '=',
    sp1: 1,
    sp2: 2,
    ep1: 2,
    ep2: 3,
    content: '2'
  }]
  
  第二个数组数据
  const newArray = [{
    type: '=',
    sp1: 0,
    sp2: 0,
    ep1: 1,
    ep2: 1,
    content: '1'
  }, {
    type: '-',
    sp1: 1,
    sp2: 1,
    ep1: 2,
    ep2: 1,
    content: '5'
  }, {
    type: '=',
    sp1: 2,
    sp2: 1,
    ep1: 3,
    ep2: 2,
    content: '2'
  }]

得到两个数组数据之后,进行对比,根据符号以及下标可判断,后面相对于前面来说做了什么操作,判断大致代码如下

先循环后面一项数组数据newArray,然后每一项去跟前面的数组数据的每一项去比较
for(let i=0;i<newArray.length;i++) {
  const new = newArray[i]
  // 寻找与当前的项对应的上一个数组中的某项
  for(let j=0;j<preArray.length;j++) {
    const pre = preArray[j]
    // 如果上一个数组的项是被删除的,则不会影响下一个数组数据
    if (pre.type === '-') {
      continue;
    }
    // 根据下标可查到当前项对应的上一个数组的哪一项
    if (pre.sp1 === new.sp2) {
      break;
    }
  }
  // 获取数组数据对比情况
  const method = preArray[j].type + new.type
  merge[method].call(preArray, j, newArray, i)
}
const merge = {
  '==': function(preArray, j, newArray, i) {
    // 替换
    const pre = preArray[j]
    const new = newArray[i]
    preArray.splice(j, 1, new)
  },
  '=+': function(preArray, j, newArray, i) {
    // 直接插入
    const pre = preArray[j]
    const new = newArray[i]
    preArray.splice(j, 0, new)
  },
  '=-': function(preArray, j, newArray, i) {
    // 替换,将type从'='替换成'-'
    const pre = preArray[j]
    const new = newArray[i]
    preArray.splice(j, 1, new)
  },
  '+=': funciton(preArray, j, newArray, i) {
    // 替换,type固定为'+'
    const pre = preArray[j]
    const new = newArray[i]
    const n = { ...new, type: '+' }
    preArray.splice(j, 1, new)
  },
  '++': function(preArray, j, newArray, i) {
    // 直接插入
    const pre = preArray[j]
    const new = newArray[i]
    preArray.splice(j, 0, new)
  },
  '+-': function(preArray, j, newArray, i) {
    // 替换
    const pre = preArray[j]
    const new = newArray[i]
    preArray.splice(j, 1, new)
  }
}

merge两个数组数据之后,合并成一个数组数据,后续的newArray又将与合并后的preArray继续调用merge进行合并。